bzoj 3998: [TJOI2015]弦论(后缀自动机)
题意:
对于一个给定长度为N的字符串,求它的第K小子串是什么。
题解:
后缀自动机O(n)*26解决。
对于op=0,num[i]=1,对于op=1,num[i]=cnt[i]。
因为cnt[i](即right集)表示以i节点结尾的后缀出现的次数。
1 #include<cstdio> 2 #include<cstring> 3 #define F(i,a,b) for(int i=a;i<=b;++i) 4 #define mst(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 7 const int N=5e5+7,tyn=26,M=N*2; 8 struct SAM{ 9 int tr[M][tyn],f[M],ml[M],ed,last,p,x,r,q; 10 int cnt[M],b[M],d[M]; 11 inline int gid(char x){return x-'a';} 12 inline void nc(int s,int &p){ 13 ml[p=++ed]=s,f[ed]=cnt[ed]=0,mst(tr[ed],0); 14 } 15 void clear(){ed=0,nc(0,last);} 16 void add(int w){ 17 nc(ml[x=last]+1,p),last=p,cnt[p]=1; 18 while(x&&!tr[x][w])tr[x][w]=p,x=f[x]; 19 if(!x)f[p]=1; 20 else if(ml[x]+1==ml[q=tr[x][w]])f[p]=q; 21 else{ 22 nc(ml[x]+1,r),f[r]=f[q],f[p]=f[q]=r; 23 memcpy(tr[r],tr[q],sizeof(tr[r])); 24 while(x&&tr[x][w]==q)tr[x][w]=r,x=f[x]; 25 } 26 } 27 void upright(int mx=0){ 28 F(i,0,ed)d[i]=0; 29 F(i,1,ed)d[mx<ml[i]?mx=ml[i]:ml[i]]++; 30 F(i,1,mx)d[i]+=d[i-1]; 31 F(i,1,ed)b[d[ml[i]]--]=i; 32 for(int i=ed;i;--i)cnt[f[b[i]]]+=cnt[b[i]]; 33 } 34 void build(char *s){for(int i=1;s[i];i++)add(gid(s[i]));} 35 36 long long sum[M]; 37 void getsum(int op=0){ 38 for(int i=ed;i;i--){ 39 int x=b[i];sum[x]=op?cnt[x]:1; 40 F(j,0,tyn-1)sum[x]+=sum[tr[x][j]]; 41 } 42 } 43 void print_min(long long k,int op=0,int rt=1) 44 { 45 if(rt!=1&&k<=(op?cnt[rt]:1))return; 46 if(rt!=1)k-=(op?cnt[rt]:1); 47 F(i,0,tyn-1)if(tr[rt][i]) 48 { 49 if(k>sum[tr[rt][i]])k-=sum[tr[rt][i]]; 50 else{ 51 printf("%c",i+'a'); 52 print_min(k,op,tr[rt][i]); 53 break; 54 } 55 } 56 } 57 }sam; 58 char s[N]; 59 int a; 60 long long b; 61 62 int main() 63 { 64 scanf("%s",s+1); 65 scanf("%d%lld",&a,&b); 66 sam.clear(),sam.build(s),sam.upright(); 67 sam.getsum(a),sam.print_min(b,a);puts(""); 68 return 0; 69 }