【BZOJ3998】弦论 [SAM]
弦论
Time Limit: 10 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S。
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。
T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input
aabc
0 3
0 3
Sample Output
aab
HINT
N<=5*10^5, T<2, K<=10^9
Solution
首先我们先构造一个后缀自动机,然后分类讨论:
1. 如果T=0,点权为1。为什么呢?一个点有一个Right集合,一个Right集合可以表达多个子串 ,然后我们一个点 -> 另外一个点 其实不止一条边,我们每条边涵盖了一个信息,意味着 这个点->(走这条边)->到达了下一个点 通过这条边得到的那个新子串,而这多个新子串构成了一个 新的点。所以一条合法的路径,就表达了一个子串。
2. 如果T=1,点权为Right集合大小。Right集合是结束位置的合集,那么Right集合大小就表示这条路径表达的这个子串出现了多少次。
Code
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cmath> 8 using namespace std; 9 typedef long long s64; 10 11 const int ONE = 2e6+5; 12 13 int n,T,k; 14 char ch[500005]; 15 16 inline int get() 17 { 18 int res=1,Q=1; char c; 19 while( (c=getchar())<48 || c>57) 20 if(c=='-')Q=-1; 21 if(Q) res=c-48; 22 while((c=getchar())>=48 && c<=57) 23 res=res*10+c-48; 24 return res*Q; 25 } 26 27 struct SAM 28 { 29 int v[500005], q[ONE], num[ONE], size[ONE]; 30 int a[ONE][28], len[ONE], fa[ONE], New; 31 int last, cnt; 32 33 SAM() {last = cnt = 1;} 34 void Add(int c) 35 { 36 int x=last, New=last=++cnt; 37 len[New] = len[x] + 1; 38 num[New] = 1; 39 while(x && !a[x][c]) a[x][c] = New, x = fa[x]; 40 if(!x) {fa[New] = 1; return;} 41 42 int q = a[x][c]; 43 if(len[x] + 1 == len[q]) fa[New] = q; 44 else 45 { 46 int Nq = ++cnt; len[Nq] = len[x] + 1; 47 memcpy(a[Nq], a[q], sizeof(a[q])); 48 fa[Nq] = fa[q]; 49 fa[New] = fa[q] = Nq; 50 while(a[x][c] == q) a[x][c] = Nq, x = fa[x]; 51 } 52 } 53 54 void Update() 55 { 56 for(int i=1;i<=cnt;i++) v[len[i]]++; 57 for(int i=1;i<=n;i++) v[i] += v[i-1]; 58 for(int i=cnt;i>=1;i--) q[v[len[i]]--] = i; 59 60 61 for(int i=cnt; i>=1; i--) 62 { 63 int x = q[i]; 64 if(!T) num[x] = 1; else num[fa[x]] += num[x]; 65 } 66 num[1] = 0; 67 68 for(int i=cnt; i>=1; i--) 69 { 70 int x = q[i]; 71 size[x] = num[x]; 72 for(int j=1; j<=26; j++) 73 size[x] += size[a[x][j]]; 74 } 75 } 76 77 void Dfs(int u,int k) 78 { 79 if(k <= num[u]) return; 80 k -= num[u]; 81 for(int j=1; j<=26; j++) 82 if(a[u][j]) 83 { 84 if(k > size[a[u][j]]) k -= size[a[u][j]]; 85 else 86 { 87 printf("%c",j+'a'-1); 88 Dfs(a[u][j], k); 89 return; 90 } 91 } 92 } 93 }S; 94 95 int main() 96 { 97 scanf("%s",ch+1); n = strlen(ch+1); 98 T = get(); k = get(); 99 for(int i=1;i<=n;i++) S.Add(ch[i]-'a'+1); 100 101 S.Update(); 102 103 if(k > S.size[1]) printf("-1"); 104 else S.Dfs(1, k); 105 106 }