Description
对于一个给定的长度为 n 的字符串,求出它的第 k 小子串是什么。
Solution
对字符串构建SAM,在parent树上求出每个点的endpos集合大小,再在正向边上求出所有子串个数,分本质不同和本质相同讨论
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int t,k,length,las=1,tot=1,f[1000005],g[1000005],buc[1000005],topo[1000005]; char s[500005]; struct Node { int ch[26],fa,len; }sam[1000005]; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return f*w; } void insert(int c) { int p=las,np=las=++tot; sam[np].len=sam[p].len+1; f[np]=1; for(;p&&!sam[p].ch[c];p=sam[p].fa) { sam[p].ch[c]=np; } if(!p) { sam[np].fa=1; } else { int q=sam[p].ch[c]; if(sam[q].len==sam[p].len+1) { sam[np].fa=q; } else { int nq=++tot; sam[nq]=sam[q]; sam[nq].len=sam[p].len+1; sam[q].fa=sam[np].fa=nq; for(;p&&sam[p].ch[c]==q;p=sam[p].fa) { sam[p].ch[c]=nq; } } } } void print(int x,int n) { if(n<=f[x]) { return; } n-=f[x]; for(int i=0;i<26;i++) { int v=sam[x].ch[i]; if(v) { if(n>g[v]) { n-=g[v]; } else { putchar(i+'a'); print(v,n); return; } } } } int main() { scanf("%s",s); length=strlen(s); t=read(); k=read(); for(int i=0;i<length;i++) { insert(s[i]-'a'); } for(int i=1;i<=tot;i++) { buc[sam[i].len]++; } for(int i=1;i<=tot;i++) { buc[i]+=buc[i-1]; } for(int i=1;i<=tot;i++) { topo[buc[sam[i].len]--]=i; } for(int i=tot;i>=1;i--) { f[sam[topo[i]].fa]+=f[topo[i]]; } if(!t) { for(int i=1;i<=tot;i++) { g[i]=f[i]=1; } } else { for(int i=1;i<=tot;i++) { g[i]=f[i]; } } f[1]=g[1]=0; for(int i=tot;i>=1;i--) { for(int j=0;j<26;j++) { if(sam[topo[i]].ch[j]) { g[topo[i]]+=g[sam[topo[i]].ch[j]]; } } } if(g[1]<k) { puts("-1"); } else { print(1,k); } return 0; }