[模板]字符串处理

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;
}

字符串哈希

posted @ 2022-07-21 00:38  th-is  阅读(46)  评论(0)    收藏  举报