后缀自动机小结

详解传送门
后缀三姐妹之一,是解决字符串问题的一大有力工具。详细介绍请看上面的传送门。

这个自动机上的节点是固定的。每个节点包含 e n d p o s endpos endpos相同的一些串。
不过有两种连边的方式。

首先是一个有向无环图。该图中每条边上对应一个字母。对于一个节点 i i i,从根到 i i i有一条路径,路上的字符构成一个字符串。这表示 i i i节点包含该字符串。一个节点可能包含多个字符串。类似于一个字典树。(下左)

其二是一颗树。每个节点的 e n d p o s endpos endpos集合是相同的。每个节点由最长的那个字符串作代表。(下右)
这个树有很多优美的性质,随便列几条把:
①一个节点到根的路径上的所有节点 包含的字符串 为该节点包含的最长串的所有后缀。以"abcbc"节点为例,该节点包含:“abcbc”,“bcbc”,“cbc”(endpos集合为{5}),它的父亲是"bc"节点,该节点包含:“bc”,“c”(endpos集合为{3,5}),“bc”节点的父亲为根节点。仔细看看,这些串是不是"abcbc"的所有后缀?
②一个节点 P P P包含的串的个数为 l e n ( P ) − l e n ( l i n k ( P ) ) len(P)-len(link(P)) len(P)len(link(P))。这里 l i n k ( P ) link(P) link(P) P P P的父亲。
③一个节点的 e n d p o s endpos endpos集合为它所有儿子的 e n d p o s endpos endpos集合的交集。换句话说,一个节点的 e n d p o s endpos endpos集合的大小为它所有儿子的 e n d p o s endpos endpos集合大小的和。
④节点数不超过 2 n − 1 2n-1 2n1
⑤转移数量不超过 3 n − 4 3n-4 3n4
⑥一个字符串建出后缀自动机,它的 p a r e n t parent parent树就是它反串的后缀树。这大概是很重要的一个性质。我们建立完自动机后,可以发现,非克隆点到根节点代表着它反串的一个后缀。而克隆点相当于是内部节点,它到根节点代表的是反串的某些后缀的最长公共前缀。凭借这一点可以做很多事情。
例串:“abcbc”
按照如上所述的构图方式,也就不难理解这个图了吧。
在这里插入图片描述

构图采用的是增量法。考虑每加一个点对原图的影响。

为什么要增加一个克隆点?
因为在那个情况下, q q q节点里面包含的部分串的 e n d p o s endpos endpos集合变了—— e n d p o s endpos endpos集合增加了一个元素,即最后一个位置。
l e n ( p ) + 1 = = l e n ( q ) len(p)+1==len(q) len(p)+1==len(q),说明 q q q中只有一个元素,也就不存在这个问题了。
而当 l e n ( p ) + 1 &lt; l e n ( q ) len(p)+1&lt;len(q) len(p)+1<len(q),那么 q q q中所含元素的 e n d p o s endpos endpos集合就会分成两派,这是不允许的。于是我们需要调整这个东西:
(以下内容中,“节点”所指字符串均为对应节点最长串)

l a s t last last节点往上走,走到 p p p节点发现 p p p节点中的元素 在后面接一个’c’可以到达 q q q节点。由于 l e n ( p ) + 1 &lt; l e n ( q ) len(p)+1&lt;len(q) len(p)+1<len(q),所以 q q q节点中不止一个元素。
由于 p p p l a s t last last的一个后缀(因为是从 l a s t last last往上走到 p p p),那么 p + p+ p+’c’一定在 l a s t last last中出现过(在有向无环图上从根走到 p p p,再从 p p p走’c’边就有了 p + p+ p+’c’)。
而现在往 l a s t last last后面加一个’c’, p + p+ p+'c’就有了新的 e n d p o s endpos endpos:最后一个位置。(注意到 p p p l a s t last last的一个后缀)但是在 q q q节点中比 p + p+ p+’c’长的那些串并没有增加 e n d p o s endpos endpos(因为 p p p是我们从 l a s t last last往上走所遇到的第一个有’c’出边的节点,所以 p p p是结尾为’c’的最长后缀)
那么就要把这两部分分开。
分开的时候,从 p p p到根的路径上的节点如果有通过’c’连向 q q q,它们的 e n d p o s endpos endpos也是会增加的(它们是 p p p的后缀)。把他们的’c’出边连向克隆点就行了。代码也大概就这样吧。

inline void build(int c){
	int cur=++sz,p=last;
	st[cur].len=st[last].len+1,st[cur].right=1;
	for(;p!=-1&&!st[p].nxt[c];p=st[p].link)
		st[p].nxt[c]=cur;
	if(p==-1) st[cur].link=0;
	else{
		int q=st[p].nxt[c];
		if(st[p].len+1==st[q].len) st[cur].link=q;
		else{
			int clone=++sz;st[clone]=st[q];
			st[clone].right=0,st[clone].len=st[p].len+1;
			for(;p!=-1&&st[p].nxt[c]==q;p=st[p].link)
				st[p].nxt[c]=clone;
			st[cur].link=st[q].link=clone;
		}
	}last=cur;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值