您的位置:首页 > 其它

bzoj3998 [TJOI2015]弦论 后缀自动机

2017-07-24 10:36 295 查看
经典的求k小子串的问题。

根据right集合的定义,如果是相同子串不同位置算作一个,那每一个right集合对应的左右区间长度的大小就是这个right集合所包含的不同子串个数。

但是知道了这个并不代表我们可以直接求出来= =因为你并不知道k小是right集合中的哪一个,也就是说你没有办法准确的用尽量小的复杂度去把这个K小的子串拿出来,所以我们只能在dag上搞事情。。

设sum[i]表示以i为根时子树不同的子串个数。

那么有f[i]=sigma(f[son])+num[i]

num[i]表示以i为根时i这个节点对f的贡献。

那么明显当不算重复时num[i]=1否则num[i]就是i这个节点伸出的边数。

然后dfs一遍像主席树一样求个k大就好了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+5;
typedef long long ll;
int cnt,tot,n,m,mx
;
char s
;
int ch
[30],fa
;
int f
,b
,last,c
,sum
;
int T,k;
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 pre()
{
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]];
}
}
inline void solve(int x,int k)
{
if (f[x]>=k)return;
k-=f[x];
fo(i,0,25)
if (ch[x][i])
{
int t=ch[x][i];
if (sum[t]>=k)
{
putchar(i+'a');
solve(ch[x][i],k);
return;
}
else k-=sum[t];
}
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
cnt=last=1;
fo(i,1,n)ins(s[i]-'a');
scanf("%d%d",&T,&k);
pre();
if (k>sum[1])printf("-1");
else solve(1,k);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: