题链:
http://www.spoj.com/problems/SUBLEX/
题解:
后缀自动机。
首先,因为相同的子串都被存在了自动机的同一个状态里面,所以这就很自然的避免了重复子串的问题。
然后考虑自动机里面的转移trans,发现其构成了一个DAG,且从一个状态出发,DFS下去就可以得到所有的不重复的串;
所以我们按照拓扑序对状态排序,然后DP计算出从每个状态出发可以到达多少个子串。
转移方程:$dp[p]=sum_{trans(p,*)=q,q!=0}dp[q]+1$
然后对于每个输入的k,在自动机里面配合dp数组去dfs就好。
代码:
#include<bits/stdc++.h> #define MAXN 90005 #define ll long long using namespace std; ll cnt[MAXN*3]; struct SAM{ int size,last; int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3]; int Newnode(int a,int b){ ++size; maxs[size]=a; memcpy(trans[size],trans[b],sizeof(trans[b])); return size; } void Extend(int x){ static int p,np,q,nq; p=last; last=np=Newnode(maxs[p]+1,0); for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np; if(!p) parent[np]=1; else{ q=trans[p][x]; if(maxs[p]+1!=maxs[q]){ nq=Newnode(maxs[p]+1,q); parent[nq]=parent[q]; parent[q]=parent[np]=nq; for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq; } else parent[np]=q; } } void Build(char *S){ memset(trans[0],0,sizeof(trans[0])); size=0; last=Newnode(0,0); for(int i=0;S[i];i++) Extend(S[i]-'a'); } }SUF; void DP(){ static int in[MAXN*3],A[MAXN*3],ant,q; queue<int>Q; for(int p=1;p<=SUF.size;p++) for(int c=0;c<26;c++){ q=SUF.trans[p][c]; if(!q) continue; in[q]++; } Q.push(1); while(!Q.empty()){ int p=Q.front(); Q.pop(); A[++ant]=p; for(int c=0;c<26;c++){ q=SUF.trans[p][c]; if(!q) continue; in[q]--; if(!in[q]) Q.push(q); } } for(int i=ant,p;i;i--){ p=A[i]; cnt[p]=(p==1?0:1); for(int c=0;c<26;c++){ q=SUF.trans[p][c]; if(!q) continue; cnt[p]+=cnt[q]; } } // printf("%lld ",cnt[1]); } void dfs(int p,int k,char from){ static int i,q; static char ans[MAXN]; if(p==1) i=0; else k--,ans[i++]=from; if(!k) return (void)(ans[i]=0,puts(ans)); for(int c=0;c<26;c++){ q=SUF.trans[p][c]; if(!q) continue; if(k<=cnt[q]){dfs(q,k,'a'+c); break;} k-=cnt[q]; } } int main(){ static char S[MAXN]; scanf("%s",S); SUF.Build(S); DP(); int Q,K; scanf("%d",&Q); while(Q--){ scanf("%d",&K); dfs(1,K,0); } return 0; }