[模板]字符串处理
KMP & ex-KMP
1、KMP
2、ex-KMP
ManaCher
AC自动机
后缀数组 倍增&DC3
后缀自动机SAM 详情见文件
后缀自动机:节点数尽可能少能接受字符串所有子串的自动机
添加新字符c:找到接受原串所有后缀对应的节点集合,向新节点连一条字符为c的边
每个节点对应若干连续长度子串(这些子串跑自动机时会到达这个节点),每个节点的父节点可接受的最长子串为该节点的最短子串 去掉首字母。
那么在添加新字符时,原串的最后一个节点last和它的所有祖先就是 原串所有后缀对应的节点集合。
在对某个节点 向 新节点连字符c的边的时候可能发现该节点已经有字符c的边了。
那么就需要拆点,拆出去的那个点要拷贝原点的所有出边,然后该点的父亲为原点父亲,另一点的父亲为该点,新添点的父亲为该点。

不用拆点(因为q实际上是有p及其所有祖先转移得到)

拆点(因为q不仅可以有p及其所有祖先得到,还会被其他更长的字符串转移得到,要把前面那部分拆出来作为np的父节点)
查看代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define LL long long
#define For(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=1e6+1000;
const int M=2e6+1000;//新加一个字符最多加2个点(一个新节点一个拆出的节点)
char s[N];
int len_0;
namespace SAM
{
int cnt=1,last=1,now,last_son;//提前插入一个根节点
int len[M];//当前节点所对应的最长子串长度,实际上也是该节点对应最长子串 在字符串中第一次出现的位置
int size[M];
int pa[M];//一个节点的父节点对应的最长子串 是 该节点对应的最短子串 去掉首字母
int son[M][30];
void insert(int c,int v)//参考wzt ppt (代码是我自己写的)
{
now=++cnt;len[now]=v;size[now]=1;
for(;last&&!son[last][c];last=pa[last]) son[last][c]=now;
//寻找原串所有后缀对应的节点集合,直到某个父节点已经有该字符对应的出边
if (!last) pa[now]=1;//没有某个节点有该字符出边,这个节点的父节点为根节点
else
{
last_son=son[last][c];
if (len[last_son]==len[last]+1) pa[now]=last_son;//last_son包含的字符串是由 当前last和其所有祖先(parent树) 转移得到
else // last_son包含的字符串不仅是由 当前last和其所有祖先(parent树) 转移得到 还有一些从其他更长的字符串转移的
{
//拆点的时候一个变量一个变量地维护
++cnt;//从last_son拆出其上半部分(由last及其所有祖先转移到的部分)作为cnt,用来作为新添点now的父节点
len[cnt]=len[last]+1; //克隆的点的最长字符串等于last的最长字符串长度+1 (即在后面添一个字符c)
For(i,1,26) son[cnt][i]=son[last_son][i]; //防止因为分割导致原last_son的孩子们的一些状态没法被last转移到
for(;son[last][c]==last_son;last=pa[last]) son[last][c]=cnt;//确保cnt包含了分割后原last上半部分的状态
pa[cnt]=pa[last_son];//cnt继承last_son的父亲
pa[last_son]=cnt;// 分割以后cnt的长度比较短,是last_son 的父亲
//拆点结束
pa[now]=cnt;
}
}
last=now;
}
};
int nex[M],Next[M],d[M],tol=0;
void Link(int x,int y)
{
Next[tol]=nex[x];
nex[x]=tol;
d[tol]=y;
tol++;
}
void Build()
{
For(i,2,SAM::cnt) Link(SAM::pa[i],i);
}
LL ans=0;
void Find(int root)
{
for(int p=nex[root];p!=-1;p=Next[p])
{
Find(d[p]);SAM::size[root]+=SAM::size[d[p]];
}
if (SAM::size[root]!=1) ans=max(ans,(LL)SAM::size[root]*SAM::len[root]);
}
int main()
{
memset(nex,-1,sizeof(nex));
scanf("%s",s+1);len_0=strlen(s+1);
For(i,1,len_0)
{
SAM::insert(s[i]-'a'+1,i);
}
Build();Find(1);
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号