我是看洛谷这篇文章(https://www.luogu.com.cn/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie)学习的,建议也看看那篇文章先。
这里我画了些图供补充理解。
提供几个便于理解的字符串:“bcaca” “aaaaa” “aababa” “aabcabc”
下面还有我带注释的代码,要是有错请提出来,我也刚学会 😃
关于case2 那个添加复制节点我有个图来理解
要是空格缩进不支持的话,附上paste.ubuntu链接 https://paste.ubuntu.com/p/hvJSFNdtZ2/
该代码可提交通过luogu这道模板题,下面注释的那一串是输出自动机的图。
https://www.luogu.com.cn/problem/P3804
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Add(x,y) nxt[++num]=head[x],v[num]=(y),head[x]=num
#define For(x) for (int h=head[x],o=v[h]; h; o=v[h=nxt[h]])
using namespace std;
const int N=1000005;
char S[N];
int L;
int head[2*N],nxt[2*N],v[2*N],num;
struct node
{
int ch[26],len,fa;
/*
ch[c] 自动机中该状态的 c 出边
len parenttree 中该 endpos 类中最长串的长度
fa parenttree 中该节点的父节点
*/
};
node a[2*N];
int tot,lst,cnt[2*N];
struct _SAM
{
/*
tot 当前节点数
lst 旧串(加入c之前)对应的状态节点
*/
//_SAM(){tot=lst=1; memset(a,0,sizeof(a)); memset(cnt,0,sizeof(cnt)); ;}
void reset()
{
tot=lst=1;
memset(a,0,sizeof(a));
memset(cnt,0,sizeof(cnt));
}
void insert(int c)
{
int p=lst,newp=++tot;
/*
p 旧串的某个后缀
np 新节点,代表新串本身
*/
a[newp].len=a[lst].len+1; //新节点状态代表加入 c 后整个串
while (p && !a[p].ch[c]) //一直跳旧串的后缀, 直到 p 有 c 边
{
a[p].ch[c]=newp; //将没有 c 边的旧串后缀节点加上 c 边
p=a[p].fa;
}
if (!p) //#1
a[newp].fa=1;
else
{
int q=a[p].ch[c];
if (a[p].len+1==a[q].len) //#2.1
a[newp].fa=q;
else //#2.2
{
int newq=++tot; //"复制" 一个 q
a[newq]=a[q];
a[newq].len=a[p].len+1;
a[q].fa=a[newp].fa=newq;
while (p && a[p].ch[c]==q) //调整 newq 使之成为相当于 #2.1 中 q 的角色
{
a[p].ch[c]=newq;
p=a[p].fa;
}
}
}
lst=newp;
}
/*
#1
意味着当前 c 是个新出现的字符
#2.1
若 q.len==p.len+1,
则 q.longest 刚好是 p.longest 后面加 c,
说明 q 中所有状态都是新串的后缀,
newp.fa 可为 q
#2.2
若 q.len!=p.len+1,
则 q.longest 是 p.longest 后面加 c, 而且前面还要加一些字符,
说明需要新建一个节点 newq, 充当 #2.1 中的 q 的角色
*/
void get_cnt()
{
memset(head,0,sizeof(head));
num=0;
for (int i=1; i<=tot; i++)
Add(a[i].len,i);
for (int i=0,tmp=1; i<L; i++)
cnt[tmp=a[tmp].ch[(int)S[i]-'a']]=1;
for (int i=L; i; i--) For(i)
cnt[a[o].fa]+=cnt[o];
}
} SAM;
int main()
{
long long ans=0;
scanf("%s",S);
L=strlen(S);
SAM.reset();
for (int i=0; i<L; i++)
SAM.insert(S[i]-'a');
SAM.get_cnt();
for (int i=1; i<=tot; i++)
if (cnt[i]>1 && a[i].len*cnt[i]>ans)
ans=a[i].len*cnt[i];
printf("%lld",ans);
/*
int L;
char S[10000];
scanf("%s",S);
L=strlen(S);
SAM.reset();
for (int i=0; i<L; i++)
SAM.insert(S[i]);
for (int i=1; i<=SAM.tot; i++)
{
printf("[%d] ->\t%d\n\t\t",i,SAM.a[i].fa);
for (int j=1; j<=100; j++)
if (SAM.a[i].ch[j])
printf("(%c) %d\n\t\t",j,SAM.a[i].ch[j]);
printf("\n\n");
}
*/
return 0;
}