SAM
后缀自动机可以帮助我们在 \(\mathcal{O}(n)\) 的图里面表示出所有的子串。
考虑最朴素的,我们将字符串的所有后缀加入字典树种,那么从源点到任意一个结点都是字符串中的一个子串。但是这样的问题是我们会有 \(\mathcal{O}(n^2)\) 级别的点,我们考虑树是不好的,我们如果能够压缩一些信息在 DAG 里维护相同的东西,就是后缀自动机要做的事。
我们考虑 \(\mathrm{endpos}(p)\) 表示一个子串 \(p\) 所有出现位置的集合。比如对于 \(\text{abcab}\) 这个字符串来说,\(\text{ab}\) 出现在 \(\textcolor{red}{ab}c\textcolor{red}{ab}\),则 \(\mathrm{endpos}(\text{ab})=\{ 2,4 \}\)。
如果两个子串的 \(\mathrm{endpos}\) 相同,则其中一个子串必然为另一个子串的后缀。
原因是显然的,设 \(\mathrm{endpos}(s)=\mathrm{endpos}(t)\),且 \(|s|\le |t|\),则它们在出现的位置必然有 \(|s|\) 出现的位置与 \(|t|\) 重叠,则一定为一个后缀。根据这个结论,我们可以推出:
对于任意两个子串 \(|s|\le |t|\),要么 \(\mathrm{endpos}(t)\subseteq\mathrm{endpos}(s)\),要么 \(\mathrm{endpos}(s)\cup \mathrm{endpos}(t)=\empty\)
\[\begin{cases} \mathrm{endpos}(t)\subseteq\mathrm{endpos}(s), & \text{s is a suffix of t} \\ , \mathrm{endpos}(s)\cup \mathrm{endpos}(t)=\empty, & \text{otherwise}\end{cases} \]
我们将所有 \(\mathrm{endpos}\) 相同的子串归到一个 \(\mathrm{endpos}\) 等价类。对于任意等价类内部按照字符串大小排序,则每个子串都是上一个子串的大小减一,且为上一个子串的后缀。
比如说对于字符串 \(\text{abcab}\),\(\textcolor{red}{abc}ab\),\(a\textcolor{red}{bc}ab\),和 \(ab\textcolor{red}{c}ab\) 的 \(\mathrm{endpos}=\{ 3 \}\),那么它们都在一个等价类里。那么根据上一个结论,等价类相等意味着均为后缀关系,那么一定是从最长的一个拥有这个等价类的子串的所有后缀也都有这个等价类。下文中记 \(\mathrm{endpos}(\mathbb{Z}^+)\) 表示这个等价类的 \(\mathrm{endpos}\)。
等价类个数级别为 \(\mathcal{O}(n)\)
我们考虑对于现在的一个子串 \(p\),如果考虑 \(p\) 前面的一个字符 \(c\),由结论 \(2\) 新的 \(\mathrm{endpos}\) 为 \(\mathrm{endpos}(p)\) 的一个子集。那么我们可以看成这是一个将 \(\mathrm{endpos}(p)\) 分裂的一个过程,那么一切都是从 \(\{ 1, \ldots ,n \}\) 分裂的,所以分裂层数一定不会比线段树的分裂方式多,则个数是 \(\mathcal{O}(n)\) 级别的。
对于下面这一张图(串是 \(\text{aababa}\)),我们可以将这么分裂出的等价类排成一颗树的情况,旁边是每个等价类的最长子串。称这棵树为 parent tree。

记类 \(a\) 中最长的子串为 \(\mathrm{len}(a)\),最短的为 \(\mathrm{minlen}(a)\),有 \(\mathrm{len}(\mathrm{fa}_a)+1=\mathrm{minlen}(a)\)
我们可以从上一个结论看出来,因为对于一个 \(p\) 在前面新添加的一个字符如果可以生成一个新的等价类,那么它也一定是最小的,且这个的长度就是 \(p\) 的长度加一。
我们考虑后缀自动机的节点就在这个 parrent tree 上,然后连边使得从源点到 \(i\) 的所有路径连成的字符串都满足它们属于 \(i\) 这个等价类的子串。
我们考虑后缀自动机应该要满足的性质:
- 从源点到任意节点的路径应该可以表示出字符串的所有子串
- 从源点到任意终止节点的路径应为原串的一个后缀
- 从源点出发的任意两条不同路径形成的字符串不同

浙公网安备 33010602011771号