Problem
Solution
建立后缀自动机,然后我们可以用类似二叉搜索树的思想。先排出拓扑序,然后将这个节点所代表的子串的个数处理出来,然后枚举第k小的是否在其中即可。
我们不妨令g[u]表示从节点u开始能代表的后缀个数,那么可以做一个拓扑动规。
我们容易得到方程:
注意在计算重复子串时,要sz[f[i]]+=sz[i]。
Code
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1000010;
int t,k,n,lst,cnt,now=1,len,pre;
int a[maxn],b[maxn],c[maxn],f[maxn],l[maxn],sz[maxn],ch[maxn][26],g[maxn];
char s[maxn>>1];
void insert(int c)
{
int p=lst,np=++cnt;
lst=np;l[np]=l[p]+1;sz[np]=1;
for(;p&&!ch[p][c];p=f[p]) ch[p][c]=np;
if(!p) f[np]=1;
else
{
int q=ch[p][c];
if(l[q]==l[p]+1) f[np]=q;
else
{
int nq=++cnt;l[nq]=l[p]+1;if(!t) sz[nq]=1;
memmove(ch[nq],ch[q],sizeof(ch[q]));
f[nq]=f[q];f[q]=f[np]=nq;
for(;ch[p][c]==q;p=f[p]) ch[p][c]=nq;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
scanf("%s%d%d",s+1,&t,&k);
n=strlen(s+1);lst=cnt=1;
for(int i=1;i<=n;i++) insert(s[i]-'a');
for(int i=1;i<=cnt;i++) c[l[i]]++;
for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
for(int i=1;i<=cnt;i++) a[c[l[i]]--]=i;
for(int i=cnt,j;t&&i;i--){j=a[i];sz[f[j]]+=sz[j];}
sz[1]=sz[0]=0;
for(int i=cnt,j;i;i--)
{
j=a[i];g[j]=sz[j];
for(int r=0;r<26;r++)
g[j]+=g[ch[j][r]];
}
if(g[1]<k){puts("-1");return 0;}
while(k>sz[now])
{
k-=sz[now];
for(int i=0;i<26;i++)
{
if(k>g[ch[now][i]]) k-=g[ch[now][i]];
else{putchar(i+'a');now=ch[now][i];break;}
}
}
putchar('\n');
return 0;
}