ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

学习笔记——SAM

2022-02-07 21:35:18  阅读:164  来源: 互联网

标签:Parent SAM MaxS Tree 笔记 学习 edp operatorname


前言

不想学博弈论不想学 SA 不想学插头 dp,学 lct 被 AxDea D 飞了,那就来学 SAM。

SAM?

SAM 是后缀自动机,名义上是后缀,但实际上它能表示出一个字符串的所有不同子串。不同于你的 \(O(n^2)\) 枚举,SAM 构造,节点和边的数量也都是 \(O(n)\) 级别的。

更具体的,SAM 表现为一张 DAG,每条边上有一个字母(类似于 AC 自动机和 trie),然后从源点开始到任意节点结束都能形成一个字符串,而这些字符串刚好都是原来字符串的子串,并且已经包含了所有的子串。

前置的定义

在构造 SAM 之前,需要知道一些定义。

endpos

我们对于一个字符串 \(S\) 的任意一个子串 \(T\),那么 \(T\) 在 \(S\) 中所有出现的位置的 结束位置 下标的集合叫做 \(T\) 的 endpos。

  • 例:对于串 \(\texttt{aabaabbcbbaab}\),那么子串 \(\texttt{aab}\) 的 endpos 是 \(\{3,6,13\}\)。记作 \(\operatorname{edp}(\texttt{aab})=\{3,6,13\}\)。

endpos 等价类

我们把 endpos 相同的串的集合称作一个 endpos 等价类,简称类。

  • 例:对于串 \(\texttt{aabaabbcbbaab}\),\(\{\texttt{ab},\texttt{aab}\}\) 是一个类,因为它们的 endpos 都是 \(\{3,6,13\}\)。

前置的引理

在构造 SAM 之前,需要知道一些引理。下面我们认为 \(S_1\) 和 \(S_2\) 是原串 \(S\) 的两个不同子串,并且有 \(|S_1|\le |S_2|\)。

Lemma 1.1

\(S_1\) 是 \(S_2\) 的后缀 \(\Rightarrow\) \(\operatorname{edp}(S_2)\subseteq\operatorname{edp}(S_1)\)

  • 很好理解,既然 \(S_1\) 是 \(S_2\) 的后缀,那么有 \(S_2\) 的地方就有 \(S_1\)。

Lemma 1.2

\(\operatorname{edp}(S_2)\cap\operatorname{edp}(S_1)\not=\varnothing\) \(\Rightarrow\) \(\operatorname{edp}(S_2)\subseteq\operatorname{edp}(S_1)\),且 \(S_1\) 是 \(S_2\) 的后缀

  • 如果两个串的 endpos 有交,那么其中一个必然是另一个的后缀,然后由 Lemma 1.1 推出 \(\operatorname{edp}(S_2)\subseteq\operatorname{edp}(S_1)\)。

  • 这条定理说明每个字符串之间 endpos 没有交,只能是相互包含的关系。

Lemma 1.3

在一个类中,找出其长度最大的串 \(u\),那么剩下的串长度从 \(u\) 开始递减。

  • 例:对于串 \(\texttt{abcdabcdd}\),在 \(\operatorname{edp}=\{4,8\}\) 类中有 \(\texttt{abcd,bcd,cd}\),可以看到长度是递减的。

  • 怎么证明?假设其中最长的串是 \(MaxS\),最短的是 \(MinS\),那么对于 \(MaxS\) 的任意一个长度大于 \(|MinS|\) 的后缀 \(Suf\),由 lemma 1.2 可以知道,这个 \(Suf\) 一定是 \(MaxS\) 的后缀,\(MinS\) 一定是 \(Suf\) 的后缀。用 Lemma 1.1 推得 \(\operatorname{edp}(MaxS)\subseteq\operatorname{edp}(Suf)\subseteq\operatorname{edp}(MinS)\),那由于 \(\operatorname{edp}(MaxS)=\operatorname{edp}(MinS)\),所以 \(\operatorname{edp}(MaxS)=\operatorname{edp}(Suf)=\operatorname{edp}(MinS)\),也就是说,\(Suf\) 一定和 \(MaxS,MinS\) 在同一类。

Parent Tree

根据 Lemma 1.2 可以发现,这些子串之间形成了一棵树形结构。我们把这个树形结构叫做 Parent Tree。

更具体地,考虑一个类中最长串 \(MaxS\),如果我们向这个串的前面加入一个字符,得到一个字符串 \(NewS\),那原来类中元素的 endpos 中的位置分成了两种,一种是在 \(\operatorname{edp}(NewS)\) 中的位置,一种是不在其中。然后我们在 \(MaxS\) 前面加上不同字符就把 endpos 中的位置分成了 \(\sum\) 种(\(\sum\) 是字符集),作为这个类的儿子节点(空的删掉),然后就构成了这棵 Parent Tree。

一棵 Parent Tree

一些说明:上面节点中的 \(s\) 表示这一类中的 \(MaxS\),也就是最长的串。

反正还是看图理解吧。那这样一棵树有什么用呢?其实在后面给出 SAM 的构造的时候会说,我们对于一个节点需要能够找到它在 Parent Tree 上的父亲。而且 SAM 上节点的意义和 Parent Tree 节点的意义是相同的,使得其节点数也和 Parent Tree 相同,从而保证复杂度(见下方引理)

其实不难发现通过枚举在 \(MaxS\) 前面加的点来构造这棵树的过程,是能够不重不漏地表达出所有原串的子串的,这和 SAM 的性质相同。

所以 SAM 是基于 Parent Tree 构造的。

接下来有几个引理:

Lemma 2.1

对于 Parent Tree 上的节点 \(u\) 和它的一个儿子 \(v\),\(u\) 中的 \(MaxS\) 的长度加一就是 \(v\) 中的 \(MinS\) 的长度。

  • 很好证明,我们通过在 \(MaxS\) 前面加一个字符的方式产生 \(u\) 的儿子,那么这个 \(NewS\) 属于 \(v\)。然后易得长度小于 \(NewS\) 的串不在 \(v\) 中(它们的 endpos 要么不是 \(v\) 所代表的,要么不止 \(v\) 所代表的)。

Lemma 2.2

Parent Tree 的点数和边数都是 \(O(n)\) 级别的。

  • 这个东西我不会证,但是会感性理解,具体可以看底下的的参考文献~

SAM 的构造

先来看代码吧。

struct node{int ch[26],fa,len;}tr[MAXN<<1];
int tot,lst;
void ins(int c){
	int p=lst,np=++tot;lst=np;
	while(p&&!tr[p].ch[c]) tr[p].ch[c]=np,p=tr[p].fa;
	if(!p) tr[np].fa=1;
	else{
		int v=tr[p].ch[c];
		if(tr[v].len==tr[p].len+1) tr[np].fa=v;
		else{
			int nv=++tot;tr[nv]=tr[np];
			tr[nv].len=tr[p].len+1;
			while(p&&tr[p].ch[c]==v) tr[p].ch[c]=nv,p=tr[p].fa;
			tr[v].fa=tr[np].fa=nv;
		}
	}
}

首先,SAM 是用增量法构造的,就是通过在后面加入一个字符,然后插入新字符串的所有后缀。这样最终必然能表出原串的所有子串。

然后我们考虑插入这些后缀的时候会对 Parent Tree 上哪些节点产生影响。很容易可以想到,就是节点表示的 endpos 中包含了原来的长度的那条链。比如,对于上面那个图,\(\{1,2,3,4,5,6,7\}\to\{1,2,4,5,7\}\to\{4,7\}\to\{7\}\) 这一条链,在插入第 \(8\) 个字符的时候会有影响。我们把这条链叫做 终止链

然后我们考虑这多出来的一个字符会产生什么影响。首先对于自动机而言,我们从终止链底向上跳,每个节点都要向新建的节点连一条边上是字符 \(c\) 的边。那如果一路畅通,那么皆大欢喜,没什么影响,只要在 Parent Tree 的根上多连一个儿子就行了,相当于是出现了一个陌生的字符,这是第一种情况;第二种情况是如果我们向上跳的过程中有一个节点 \(p\),已经有边上是 \(c\) 的边连到另一个节点 \(v\) 了,说明有重复的子串了。那这个时候我们需要多维护一个东西来进一步地分讨。

我们维护每个节点的 \(MaxS\),代码中记为 \(len\)。在上面这种情况下,如果有 tr[p].len+1==tr[v].len,那么说明,在 Parent Tree 中,还没有比加字符产生的新后缀更长的,并且同样属于 \(v\) 的一个后缀。

标签:Parent,SAM,MaxS,Tree,笔记,学习,edp,operatorname
来源: https://www.cnblogs.com/ZCETHAN/p/15869445.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有