BZOJ3998: [TJOI2015]弦论【SAM】
3998: [TJOI2015]弦论
【题目描述】
【题解】
先用SAM算出不同子串个数,然后二分查找就可以了(像平衡树一样的查找第K小方式)
【代码如下】
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=5e5+5;
int n,TT,K,Sum[MAXN<<1],Hsh[MAXN<<1],Sa[MAXN<<1];char ch[MAXN];
struct SAM{
int cnt,lst,Son[MAXN<<1][27],Len[MAXN<<1],Siz[MAXN<<1],Fa[MAXN<<1];
SAM(){cnt=lst=1;}
void Insert(int c){
int fa=lst,p=++cnt;lst=cnt;Len[p]=Len[fa]+1;Siz[p]=1;
for(;fa&&!Son[fa][c];fa=Fa[fa]) Son[fa][c]=p;
if(!fa){Fa[p]=1;return;}int x=Son[fa][c];
if(Len[fa]+1==Len[x]){Fa[p]=x;return;}
int y=++cnt;Fa[y]=Fa[x],Fa[x]=Fa[p]=y;Len[y]=Len[fa]+1;
for(int i=0;i<26;++i) Son[y][i]=Son[x][i];
for(;fa&&Son[fa][c]==x;fa=Fa[fa]) Son[fa][c]=y;
}
}T;
void DFS(int x,int k){
if(k<=T.Siz[x]) return;k-=T.Siz[x];
for(int j=0;j<26;++j)
if(k<=Sum[T.Son[x][j]]){putchar(j+'a'),DFS(T.Son[x][j],k);break;}else k-=Sum[T.Son[x][j]];
}
int main(){
scanf("%s",ch+1);n=strlen(ch+1);
for(int i=1;i<=n;++i) T.Insert(ch[i]-'a');
scanf("%d%d",&TT,&K);
for(int i=1;i<=T.cnt;++i) ++Hsh[T.Len[i]];
for(int i=1;i<=n;++i) Hsh[i]+=Hsh[i-1];
for(int i=1;i<=T.cnt;++i) Sa[Hsh[T.Len[i]]--]=i;
for(int i=T.cnt,p;p=Sa[i],i;--i) TT?T.Siz[T.Fa[p]]+=T.Siz[p]:T.Siz[p]=1;
T.Siz[1]=0;
for(int i=T.cnt,p;i;--i){
Sum[p=Sa[i]]=T.Siz[p];
for(int j=0;j<26;j++) Sum[p]+=Sum[T.Son[p][j]];
}
if(K>Sum[1]) return printf("-1\n"),0;return DFS(1,K),0;
}