SAM(后缀自动机)速通
约定
记 \(\Sigma\) 为字符集,\(|\Sigma|\) 为字符集大小。
定义 & 定理
SAM 是一个接受 \(s\) 所有后缀的最小 DFA。
定义 \(\mathrm{endpos}(t)\) 表示字符串 \(t\) 在 \(s\) 所有的结束位置(从 \(0\) 开始标号),方便起见,有 \(\mathrm{endpos}(t_{0})\) 定义为 \(\{ -1,0,\dots,|S|-1 \}\)。
我们将所有 \(\mathrm{endpos}\) 相等的字符串划分至同一个等价类;
引理 1: 若字符串 \(s\) 的两个子串 \(u,v\;(|u|\leq|v|)\) 的 \(\mathrm{endpos}\) 相同,当且仅当 \(u\) 为 \(v\) 的后缀;
引理 2: 对于字符串 \(s\) 的两个子串 \(u,v\;(|u|\leq|v|)\),满足
\[\begin{cases} \mathrm{endpos}(v)\subseteq \mathrm{endpos}(u) &\text{if } u\text{ is a suffix of } v \\ \mathrm{endpos}(v)\cap \mathrm{endpos}(u)=\varnothing &\text{otherwise} \end{cases} \]
引理 3: 一个状态的字符串的长度恰好唯一覆盖 \([\mathrm{minlen}(u), \mathrm{len}(u)]\),且长度短的字符串为长度长的字符串的后缀。
显然,SAM 由 \(t_{0}\) 和每个等价类对应状态构成。
我们令后缀链接 \(\mathrm{link}(u)\) 链接至最长的 \(\mathrm{endpos}\) 不同的后缀所对应的等价类。
引理 4: 所有的后缀链接构成一颗以\(t_{0}\) 为根的树(即为后缀树)。
引理 5: 对于 \(t_{0}\) 以外的状态 \(v\),有 \(\mathrm{minlen}(v)=\mathrm{len}(\mathrm{link}(v))+1\)。
引理 6: 任意状态 \(v_{0}\) 顺着 \(\mathrm{link}\) 遍历,总会到达 \(t_{0}\)。路径中我们可以得到互不相交的区间序列 \([\mathrm{minlen}(v_{i}),\mathrm{len}(v_{i})]\),且并集为 \([0, \mathrm{len}(v_{0})]\)。
算法
该算法为在线算法,且为保证空间复杂度,我们只保存 \(\mathrm{len}\) 和 \(\mathrm{link}\) 的值,而非 \(\mathrm{endpos}\)。
初始 SAM 只包含一个状态 \(t_{0}\),编号为 \(0\),并指定其 \(\mathrm{len}=0,\mathrm{link}=-1\)(\(-1\) 为虚拟状态)。
流程
- 令 \(last\) 为添加字符 \(c\) 前,整个字符串对应的状态(初始 \(last=0\))。
- 创建新状态 \(cur\),令 \(\mathrm{len}(cur)=\mathrm{len}(last)+1\)。
- 从状态 \(last\) 开始,不停向后缀链接遍历直至其存在到字符 \(c\) 的转移或已到达虚拟结点 \(-1\),记该状态为 \(p\)。将遍历中的状态(除了 \(p\))向 \(cur\) 添加转移。
- 若 \(p=-1\),令 \(\mathrm{link}(cur)=0\) 并退出。
- 令 \(p\) 通过 \(c\) 转移得到的状态为 \(q\),分两种情况讨论:
- 若 \(\mathrm{len}(p)+1=\mathrm{len}(q)\),令 \(\mathrm{link}(cur)=q\) 并退出;
- 否则,我们创建新状态 \(clone\),并令其复制 \(q\) 的状态,再令 \(\mathrm{len}(clone)=\mathrm{len}(p)+1,\mathrm{link}(cur)=\mathrm{link}(q)=clone\)。最终我们通过后缀链接将 \(p\) 往回走,只要存在 \(p\to q\),就将转移重定向至 \(clone\)。
- 以上三种情况,在退出后令 \(last=cur\)。
证明
正确性证明
这是速通,有什么证明。
复杂度证明
复杂度线性,不予证明。
状态数证明
对于一个长度为 \(n\) 的字符串,状态数不超过 \(2n-1\;(n\leq 2)\)。易证。
转移数证明
对于一个长度为 \(n\) 的字符串,转移数不超过 \(4n-3\;(n\leq 3)\)。不予证明。

浙公网安备 33010602011771号