后缀自动机学习笔记--构建方法

本文详细介绍了后缀自动机(SAM)的线性复杂度构建方法,包括插入字符的过程、不同情况的处理方式以及如何维护自动机的结构。通过具体的实现步骤和代码示例,帮助读者理解SAM构建原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

续了一个晚上终于搞懂后缀自动机的建法了。。

首先,SAM的线性复杂度构建方法是将所建字符串的字符从左至右一个个插入,设插入后自动机有n个点,那么插入会产生到1~n、2~n、3~n直至n~n这些点(都是由i~n-1转移来),而在插入过程中要分成三种情况讨论。

首先因为最长长度n的点之前不可能存在,所以必然要先开一个新点(np)。再从之前一个插入的点(记为p),沿着fa边往回跳,边回跳边将没有连向np字符的点连上,如果跳到的点已经存在了转移到np这个字符的边break-->2.情况 。。不然,如果直到跳完也不存在上述2情况则-->1情况

1.该情况说明插入的字符是当前自动机里没有的字符

显然,因为插入一个没有的字符那么在它之前不可能存在其他该字符的endpos,所以它的fa必然只能连到根,故从之前一个插入的字符位置沿着fa边一个个向该字符位置连边即可。

2.说明插入的字符是已有的

这种情况需再分两种。

设该点为p,沿np相同字符所达点为t

首先跳到p存在走到np相同字符的边那么肯定不能再连np否则不满足自动机结构,而因为是后缀自动机,那么这个点之前的点显然也已经存在走到t的边,这时候如果p的最长字符长度+1等于t的最长长度,那么说明t及之前的状态不会因加入np改变,将np连fa边到t就行。还有一种情况则是p最长字符+1!=t最长长度,那么很复杂,t里的一些状态会因为np的到来改变,这时就要新建一个点,将t中改变的状态连到这个新点nt,而nt这个点的转移状态因与t相同,所以memcpy复制过来,之后再将nt的fa连向当前t的fa,np的fa和t的fa都指向nt,再将p中通过np相同字符转移到t的连向nt,并重复更新p之前的点即可。

复杂度证明的话空间显然每次最多开两个点是O(n)的,时间嘛。。反正有大佬证明了是均摊O(1)的,总的也是O(n)。

代码:

struct Sam{
	int nxt[N][26],mx[N],fa[N],las,tot;
	Sam(){las=tot=1;}
	void ins(int x)
	{
		int np,p;
		p=las,np=++tot,las=np;
		mx[np]=mx[p]+1;
		while(p&&!nxt[p][x])nxt[p][x]=np,p=fa[p];
		if(!p){fa[np]=1;return;}
		int t=nxt[p][x];
		if(mx[t]==mx[p]+1)fa[np]=t;
		else
		{
			int nt=++tot;
			mx[nt]=mx[p]+1;
			memcpy(nxt[nt],nxt[t],sizeof nxt[t]);
			fa[nt]=fa[t],fa[t]=fa[np]=nt;
			for(;nxt[p][x]==t;p=fa[p])nxt[p][x]=nt;
		}
	}
	//int cal(){return mx[las]-mx[fa[las]];}
}sam;

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值