版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/84679451
题意:
给你一个n个字符的字符串,求所有出现次数大于1的子串乘子串长度的最大值。n<=1e6
题解:
感觉全机房都早就会后缀自动机了啊,就我不会。而且感觉自己学得还不怎么样,似乎不是那么明白,感觉药丸。
这个题的话建出后缀自动机之后,我们根据SAM的性质可知,一个点在parent树的子树中的叶子节点个数就是根到该节点的子串出现的次数。我们在建后缀自动机的时候已经求出了从根到每个节点的字符串长度,那么我们就在parent树上dfs一遍,一边更新子树内叶子数,一边乘当前的字符串长度更新答案即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,fa[4000010],ch[4000010][27],len[4000010];
char s[1000010];
int hed[4000010],sz[4000010],rt=1,cnt=1,lst=1,num;
struct node
{
int to,next;
}a[8000010];
long long ans;
inline void insert(int x)
{
int cur=++cnt,pre=lst;
lst=cur;
len[cur]=len[pre]+1;
sz[cur]=1;
for(;pre&&!ch[pre][x];pre=fa[pre])
ch[pre][x]=cur;
if(!pre)
fa[cur]=rt;
else
{
int ji=ch[pre][x];
if(len[ji]==len[pre]+1)
fa[cur]=ji;
else
{
int gg=++cnt;
len[gg]=len[pre]+1;
memcpy(ch[gg],ch[ji],sizeof(ch[ji]));
fa[gg]=fa[ji];
fa[ji]=fa[cur]=gg;
for(;pre&&ch[pre][x]==ji;pre=fa[pre])
ch[pre][x]=gg;
}
}
}
inline void add(int from,int to)
{
a[++num].to=to;
a[num].next=hed[from];
hed[from]=num;
}
inline void dfs(int x)
{
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
dfs(y);
sz[x]+=sz[y];
}
if(sz[x]>1)
ans=max(ans,(long long)sz[x]*len[x]);
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i)
insert(s[i]-'a');
for(int i=2;i<=cnt;++i)
add(fa[i],i);
dfs(1);
printf("%lld\n",ans);
return 0;
}