后缀树, 后缀自动机
定义 \(endpos(s)\) 表示对于某个字串 \(s\) 在原串中的出现位置集合。
\(endpos\) 性质。
我们有两个原串的字串 \(s, t\), \(|s| \le |t|\), \(s\) != \(t\)
-
若 \(endpos(t) \in endpos(s)\), 则 \(s\) 为 \(t\) 的后缀。
证明
``` 存在一个位置 $x$, 满足 $x \in endpos(s)$ 且 $x \in endpos(t)$, 则在位置 $x$ 前面我们可以找到这两个串。 ``` -
若 \(s\) 为 \(t\) 的后缀, 则 \(endpos(t) \in endpos(s)\)
证明
``` 若存在一个 $x$, 满足 $x \in endpos(t)$ 且 $x \notin endpos(s)$, 则位置 [x - s.size() + 1, x] 为 $s$, 所以 $x \in endpos(s)$, 矛盾 ``` -
\(endpos(s)\) 与 \(endpos(t)\) 要么包含, 要么不交。
证明
``` 则存在一个位置 $x$, 满足 $x \in endpos(s)$, $x \in endpos(t)$, 类似性质 1 的证明, 可以得到 $s$ 为 $t$ 的后缀, 再通过性质2, 得 $endpos(t) \in endpos(s)$, 矛盾 ```
我们可以建出一颗 \(endpos\) 树(通过性质3), 每个节点父亲 \(endpos\) 包含这个节点。
一些关于这个树的性质
- 对于树上的每个节点, 表示的串长度连续且从大到小为后缀关系。可以通过反证法证明。
- 对于树上每个节点的最小值, 去除开头则为这个节点父亲的最长串。
我们记 \(len_u\) 表示一个节点 \(i\) 对应的后缀中最长串的长度, \(ch_{u, c}\) 表示考虑在 \(u\) 最长串的后面加一个字符 \(c\) 后转移到这颗树的最小值。
若不存在对应的点, 则 \(ch_{u, c}\), 则 \(ch_{u, c} = 0\)
证明一定转移到树上的一个点
其实发现就是 $endpos$ 集合集体加 1, 所以 $endpos$ 相同我们可以对于每个点记录一个所有串的最后一个字符位置, 就可以确定这些串。
构造
我们假设处理出前 \(i\) 个字符, 前 \(i\) 个字符组成了字符串 \(s\), 考虑加入一个字符 \(c\) 后后缀自动机的变化。
我们令 \(T\) 为一个最长的(s+c)后缀, 满足 \(T\) 为 \(s\) 的字串。
则操作等价于对于所有 \(T\) 的后缀, \(endpos\) 加入 \(\{|s| + 1\}\), 对于 \(s+c\) 到 \(T\) 的后缀, 原来没有出现过, 所以 \(endpos\) 集合为 \(\{|s| + 1\}\)
我们令 \(u\) 为 T 所在的 \(endpos\) 节点, 所以对于 \(u\) 的祖先, \(endpos\) 树上的结构不变。
对于节点 \(u\), 我们可以分成为 \(T\) 的后缀和不为 \(T\) 的后缀, 我们发现为 \(T\) 的后缀的长度一定是一段前缀, 所以我们可以考虑把这个点分成两个点, 然后就可以处理了。
我们考虑如何找到节点 \(u\), 我们令当前 \(s\) 所在的节点为 \(p\), 则不断调 \(fa_p\), 知道找到 \(ch_{p, c} > 0\), 对于一路上的 \(p\), 我们令 $ch_{u, c} = $ \((s + c)\) 对应的点。
对于那些 \(endpos\) 为 \(\{|s| + 1\}\), 父亲为 \(u\)。
时间复杂度
我们考虑 \(p\) 的深度。
对于一次跳父亲, 会让 \(dep_p \to dep_p - 1\).
若我们找到对应的点后, 会让 \(dep_p \to dep_p + 1\)。
所以时空复杂度 \(O(n)\)。

浙公网安备 33010602011771号