Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input
aabc
0 3
Sample Output
aab
Solution
后缀自动机模板题。
先建出后缀自动机,预处理出(sum[i])表示从自动机上的(i)点出发可以得到多少个串。
要预处理这个,先要预处理出(sz[i])表示自动机上第(i)个点代表的(endpos)集合的大小。
对于(T=0),直接每个(sz[i]=1)就行了,否则就在(parent)树上递推就好了。
然后得到这个就可以在自动机上递推(sum[i])了。
tips:后缀自动机的(toposort)可以按(maxl)进行桶排得到。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
const int maxn = 1e6+10;
char s[maxn];
int qs=1,cnt=1,lstp=1,n,T,rk;
int tr[maxn][27],par[maxn],ml[maxn],sz[maxn],r[maxn],t[maxn],sum[maxn];
void append(int c) {
int p=lstp,np=++cnt;ml[np]=ml[p]+1;lstp=np;sz[np]=1;
for(;p&&tr[p][c]==0;p=par[p]) tr[p][c]=np;
if(!p) return par[np]=qs,void();
int q=tr[p][c];
if(ml[p]+1<ml[q]) {
int nq=++cnt;ml[nq]=ml[p]+1;
memcpy(tr[nq],tr[q],sizeof tr[nq]);
par[nq]=par[q],par[q]=par[np]=nq;
for(;p&&tr[p][c]==q;p=par[p]) tr[p][c]=nq;
}else par[np]=q;
}
void print(int x,int k) {
if(k>sz[x]) k-=sz[x];
else return ;
for(int i=1;i<=26;i++)
if(tr[x][i]) {
if(k>sum[tr[x][i]]) k-=sum[tr[x][i]];
else {
putchar(i+'a'-1);
print(tr[x][i],k);break;
}
}
}
int main() {
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;i++) append(s[i]-'a'+1);
for(int i=1;i<=cnt;i++) t[ml[i]]++;
for(int i=1;i<=n;i++) t[i]+=t[i-1];
for(int i=1;i<=cnt;i++) r[t[ml[i]]--]=i;
read(T),read(rk);
for(int i=cnt;i;i--) T?sz[par[r[i]]]+=sz[r[i]]:sz[r[i]]=1;
sz[qs]=0;
for(int i=cnt;i;i--) {
sum[r[i]]=sz[r[i]];
for(int j=1;j<=26;j++) if(tr[r[i]][j]) sum[r[i]]+=sum[tr[r[i]][j]];
}
if(sum[1]>=rk) print(1,rk),puts("");else puts("-1");
return 0;
}