题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=3998
题解:
后缀自动机。
当T=0时,
由于在后缀自动机上沿着trans转移,每个串都是互不相同的,
就只需要统计出从每个状态出发,存在多少条不同的路径,即有多少个不同的子串。
这个可以拓扑排序后用DP解决。
转移: $$cnt[p]=\sum_{trans[p][*]=q,q!=0} cnt[q] + 1$$
然后配合cnt[]去dfs即可。
当T=1时,
这时考虑了相同的子串。但是不难发现,
如果沿着trans转移到了一个状态s,
那么s的Right集合大小就表示了这个串出现了多少次。
所以沿用上面T=0的做法,只是把DP转移稍稍修改一下:
转移: $$cnt[p]=\sum_{trans[p][*]=q,q!=0} cnt[q] + right[p]$$
(把+1改为+right[p即可],接下来同样是配合cnt[]去dfs。)
(本人代码跑得很慢,2333)
代码:
#include<bits/stdc++.h> #define MAXN 500005 #define ll long long using namespace std; ll cnt[MAXN*3]; struct SAM{ int size; int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3],right[MAXN*3]; int Newnode(int a,int b){ ++size; maxs[size]=a; memcpy(trans[size],trans[b],sizeof(trans[b])); return size; } int Extend(int last,int x){ static int p,np,q,nq; p=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; } return np; } void Build(char *S){ static int p=1,last,tmp[MAXN],order[MAXN*3],len; memset(trans[0],0,sizeof(trans[0])); size=0; last=Newnode(0,0); len=strlen(S); for(int i=0;i<len;i++) last=Extend(last,S[i]-‘a‘); for(int i=0;i<len;i++) p=trans[p][S[i]-‘a‘],right[p]++; for(int i=1;i<=size;i++) tmp[maxs[i]]++; for(int i=1;i<=len;i++) tmp[i]+=tmp[i-1]; for(int i=1;i<=size;i++) order[tmp[maxs[i]]--]=i; for(int i=size;i;i--) p=order[i],right[parent[p]]+=right[p]; } void Count(int t){ static queue<int> Q; static int in[MAXN*3],order[MAXN*3],ont; for(int p=1;p<=size;p++) for(int c=0;c<26;c++) if(trans[p][c]) in[trans[p][c]]++; Q.push(1); while(!Q.empty()){ int u=Q.front(); Q.pop(); order[++ont]=u; for(int c=0;c<26;c++) if(trans[u][c]){ in[trans[u][c]]--; if(!in[trans[u][c]]) Q.push(trans[u][c]); } } for(int i=size,p;i;i--){ p=order[i]; cnt[p]=p==1?0:t==0?1:right[p]; for(int c=0;c<26;c++) if(trans[p][c]) cnt[p]+=cnt[trans[p][c]]; } } }SUF; void DFS(int p,int k,int t,int from){ static int i; static char ans[MAXN]; if(p==1) i=0; else{ ans[i++]=from+‘a‘; k-=t==0?1:SUF.right[p]; } if(k<=0) return (void)(ans[i]=0,puts(ans)); for(int c=0;c<26;c++){ if(k<=cnt[SUF.trans[p][c]]){ DFS(SUF.trans[p][c],k,t,c); break; } k-=cnt[SUF.trans[p][c]]; } } int main(){ int T,K; static char S[MAXN]; scanf("%s%d%d",S,&T,&K); SUF.Build(S); SUF.Count(T); // printf("%lld\n",cnt[1]); if(K<=cnt[1]) DFS(1,K,T,0); else puts("-1"); return 0; }