弦论
Time Limit: 10 Sec Memory Limit: 256 MB
Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
第一行是一个仅由小写英文字母构成的字符串S。
  第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。
  T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
aabc
 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
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 
 | #include<bits/stdc++.h>using namespace std;
 typedef long long s64;
 
 const int ONE = 2e6+5;
 
 int n,T,k;
 char ch[500005];
 
 inline int get()
 {
 int res=1,Q=1;  char c;
 while( (c=getchar())<48 || c>57)
 if(c=='-')Q=-1;
 if(Q) res=c-48;
 while((c=getchar())>=48 && c<=57)
 res=res*10+c-48;
 return res*Q;
 }
 
 struct SAM
 {
 int v[500005], q[ONE], num[ONE], size[ONE];
 int a[ONE][28], len[ONE], fa[ONE], New;
 int last, cnt;
 
 SAM() {last = cnt = 1;}
 void Add(int c)
 {
 int x=last, New=last=++cnt;
 len[New] = len[x] + 1;
 num[New] = 1;
 while(x && !a[x][c]) a[x][c] = New, x = fa[x];
 if(!x) {fa[New] = 1; return;}
 
 int q = a[x][c];
 if(len[x] + 1 == len[q]) fa[New] = q;
 else
 {
 int Nq = ++cnt; len[Nq] = len[x] + 1;
 memcpy(a[Nq], a[q], sizeof(a[q]));
 fa[Nq] = fa[q];
 fa[New] = fa[q] = Nq;
 while(a[x][c] == q) a[x][c] = Nq, x = fa[x];
 }
 }
 
 void Update()
 {
 for(int i=1;i<=cnt;i++) v[len[i]]++;
 for(int i=1;i<=n;i++) v[i] += v[i-1];
 for(int i=cnt;i>=1;i--) q[v[len[i]]--] = i;
 
 
 for(int i=cnt; i>=1; i--)
 {
 int x = q[i];
 if(!T) num[x] = 1; else num[fa[x]] += num[x];
 }
 num[1] = 0;
 
 for(int i=cnt; i>=1; i--)
 {
 int x = q[i];
 size[x] = num[x];
 for(int j=1; j<=26; j++)
 size[x] += size[a[x][j]];
 }
 }
 
 void Dfs(int u,int k)
 {
 if(k <= num[u]) return;
 k -= num[u];
 for(int j=1; j<=26; j++)
 if(a[u][j])
 {
 if(k > size[a[u][j]]) k -= size[a[u][j]];
 else
 {
 printf("%c",j+'a'-1);
 Dfs(a[u][j], k);
 return;
 }
 }
 }
 }S;
 
 int main()
 {
 scanf("%s",ch+1); n = strlen(ch+1);
 T = get();    k = get();
 for(int i=1;i<=n;i++) S.Add(ch[i]-'a'+1);
 
 S.Update();
 
 if(k > S.size[1]) printf("-1");
 else S.Dfs(1, k);
 
 }
 
 |