您的位置:首页 > 其它

bzoj 3998: [TJOI2015]弦论 后缀自动机

2017-02-24 20:16 267 查看

题意

对于一个给定长度为N的字符串,求它的第K小子串是什么。

n<=5*10^5

分析

再次见识到了sam的各种妙用。

首先对原串建一棵后缀自动机,然后每走一步就相当于枚举了一个子串。用一个f[i]记录节点i的right集大小,sum[i]记录以i为根可以得到多少个子串,然后递归查找答案即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;

int n,fa
,b
,c
,f
,mx
,ch
[30],cnt,ansl,ansr,k,T,last,sum
;
char s
;

void ins(int x)
{
int p,q,np,nq;
p=last;last=np=++cnt;mx[np]=mx[p]+1;f[np]=1;
for (;!ch[p][x]&&p;p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++cnt;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}

void prework()
{
for (int i=1;i<=cnt;i++) b[mx[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=1;i<=cnt;i++) c[b[mx[i]]--]=i;
for (int i=cnt;i>=1;i--)
{
if (T==1) f[fa[c[i]]]+=f[c[i]];
else f[c[i]]=1;
}
f[1]=0;
for (int i=cnt;i>=1;i--)
{
int t=c[i];sum[t]=f[t];
for (int j=0;j<26;j++)
sum[t]+=sum[ch[t][j]];
}
}

void solve(int now,int k)
{
if (f[now]>=k) return;
k-=f[now];
for (int i=0;i<26;i++)
if (ch[now][i])
{
int t=ch[now][i];
if (sum[t]>=k)
{
putchar(i+'a');
solve(ch[now][i],k);
return;
}
else k-=sum[t];
}
}

int main()
{
scanf("%s",s+1);
n=strlen(s+1);
cnt=last=1;
for (int i=1;i<=n;i++) ins(s[i]-'a');
scanf("%d%d",&T,&k);
prework();
if (k>sum[1]) printf("-1");
else solve(1,k);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: