题目:
题意:
f(i)表示的是长度为i的子串的最大出现次数,求f(1..n)
题解:
这类问题大多数是利用Right集合的性质,因为一个节点代表的字符串一定在Right集合包含的所有位置上重复出现。想要得到关于每个节点Right集合的信息,只需要在建立自动机以后按照拓扑序从后往前,用每个节点来更新它的fa节点就可以了。
对于这道题目来说,它要求每个长度的串出现次数的最大值,那么只需要对于每个节点递推出它的Right集合大小,然后映射到长度上面去就可以了。需要注意的一个问题就是如果长度为i的串出现了k次,那么长度为i-1的串一定至少出现了k次,因为可以取那些串的后缀。所以最后还要用F(i)去更新一下F(i-1)。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=500005;
int ch[N*2][30],step[N],last,q,nq,p,np,cnt,fa[N],r[N],c[N],a[N],f[N];
char st[N];
void insert(int c)
{
p=last; last=np=++cnt;
step[np]=step[p]+1;
while (p && !ch[p][c]) ch[p][c]=np,p=fa[p];
if (!p) {fa[np]=1;return;}
q=ch[p][c];
if (step[q]==step[p]+1){fa[np]=q;return;}
nq=++cnt; step[nq]=step[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q]; fa[np]=fa[q]=nq;
while (ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
int main()
{
scanf("%s",st+1);
int l=strlen(st+1);
last=cnt=1;
for (int i=1;i<=l;i++) insert(st[i]-'a');
for (int i=1;i<=cnt;i++) c[step[i]]++;
for (int i=1;i<=cnt;i++) c[i]+=c[i-1];
for (int i=1;i<=cnt;i++) a[c[step[i]]--]=i;
p=1;
for (int i=1;i<=l;i++) p=ch[p][st[i]-'a'],r[p]++;
for (int i=cnt;i;i--)
{
int t=a[i];
r[fa[t]]+=r[t];
f[step[t]]=max(f[step[t]],r[t]);
}
for (int i=l-1;i;i--) f[i]=max(f[i],f[i+1]);
for (int i=1;i<=l;i++) printf("%d\n",f[i]);
}