题链:
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\n",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;
}