SPOJ SUBLEX
SUBLEX - Lexicographical Substring Search
题意
求第k小的子串。相同的算一个。
分析
建立后缀自动机,在后缀自动机上从一个点经过trans,到另一个点,trans会对应一个子串。而且会对应所有的子串。
每个节点能经过trans到达的点,即它可以形成的子串。所有按照拓扑序更新每个节点能形成多少个子串,如果经过当前点形成的串小于k,那么说明第k小的串不经过高这个点,k-=siz,继续找,如果小于这个的siz,那么就经过这个点,输出。有点像二叉搜索树的查询。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 inline int read() { 6 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 7 for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 8 } 9 10 const int N = 200100; 11 12 struct SuffixAutomaton{ 13 int Last, Index, fa[N], len[N], trans[N][26]; 14 int v[N],sa[N],siz[N]; 15 char s[N]; 16 void extend(int c) { 17 int P = Last, NP = ++Index; 18 len[NP] = len[P] + 1; 19 for (; P&&!trans[P][c]; P=fa[P]) trans[P][c] = NP; 20 if (!P) fa[NP] = 1; 21 else { 22 int Q = trans[P][c]; 23 if (len[P] + 1 == len[Q]) fa[NP] = Q; 24 else { 25 int NQ = ++Index; 26 fa[NQ] = fa[Q]; 27 len[NQ] = len[P] + 1; 28 memcpy(trans[NQ], trans[Q], sizeof trans[Q]); 29 fa[Q] = NQ; 30 fa[NP] = NQ; 31 for (; P&&trans[P][c]==Q; P=fa[P]) trans[P][c] = NQ; 32 } 33 } 34 Last = NP; 35 } 36 void build() { 37 Last = Index = 1; //---老是忘记这里。。。 38 scanf("%s",s+1); 39 int n = strlen(s+1); 40 for (int i=1; i<=n; ++i) extend(s[i] - 'a'); 41 for (int i=1; i<=Index; ++i) v[len[i]] ++; 42 for (int i=1; i<=n; ++i) v[i] += v[i-1]; 43 for (int i=1; i<=Index; ++i) sa[ v[len[i]]-- ] = i; 44 } 45 46 void init() { 47 for (int i=Index; i>=1; --i) { 48 int t = sa[i]; 49 siz[t] ++; 50 for (int j=0; j<26; ++j) { 51 if (trans[t][j]) siz[t] += siz[trans[t][j]]; 52 } 53 } 54 } 55 void query(int k) { 56 int p = 1; 57 while (k) 58 for (int i=0; i<26; ++i) 59 if (trans[p][i]) 60 if (k > siz[trans[p][i]]) k -= siz[trans[p][i]]; 61 else { 62 printf("%c",i+'a'); 63 p = trans[p][i]; 64 k--; 65 break; 66 } 67 } 68 void solve() { 69 build(); 70 init(); 71 int m = read(),k; 72 while (m--) { 73 k = read(); 74 query(k); 75 puts(""); 76 } 77 } 78 }sam; 79 80 int main() { 81 sam.solve(); 82 return 0; 83 }