bzoj3998/洛谷3975 [TJOI2015]弦论
题意:求第 k 小字串
方法:先建立SAM,和上一题一样按照拓扑序求出size(表示这个串出现的次数)。这道题我们需要求第 k 小,所以我们还要求出每个点下面一共有多少个串。搜第 k 小就dfs深搜就可以了。
#include <cstdio>
#include <cstring>
#define N 500010
int n,m,last,root,t,k,son[N<<1][26],fa[N<<1],mx[N<<1],size[N<<1],c[N<<1],sum[N<<1],a[N<<1];
char str[N];
inline void ins(int ch){
int p=last,np=++n;last=n;mx[np]=mx[p]+1;
while(p && !son[p][ch]) son[p][ch]=np,p=fa[p];
if(!p) fa[np]=root;
else{
int q=son[p][ch];
if(mx[p]+1==mx[q]) fa[np]=q;
else{
int nq=++n;mx[nq]=mx[p]+1;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q];fa[np]=fa[q]=nq;
while(son[p][ch]==q) son[p][ch]=nq,p=fa[p];
}
}size[np]++;
}
void dfs(int p,int s){
if(s<=size[p]) return;
s-=size[p];
for(int i=0;i<26;i++){
if(son[p][i]){
if(s<=sum[son[p][i]]){
putchar(i+'a');
dfs(son[p][i],s);
return;
}s-=sum[son[p][i]];
}
}
}
int main(){
scanf("%s%d%d",str+1,&t,&k);
int m=strlen(str+1);last=root=++n;
for(int i=1;i<=m;i++) ins(str[i]-'a');
for(int i=1;i<=n;i++) c[mx[i]]++;
for(int i=1;i<=n;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) a[c[mx[i]]--]=i;
for(int i=n;i>=1;i--){
int p=a[i];
if(t==1) size[fa[p]]+=size[p];
else size[p]=1;
}size[1]=0;
for(int i=n;i>=1;i--){
int p=a[i];sum[p]=size[p];
for(int j=0;j<26;j++) sum[p]+=sum[son[p][j]];
}if(sum[1]>=k) dfs(1,k);
else puts("-1");
return 0;
}