后缀树, 后缀自动机

定义 \(endpos(s)\) 表示对于某个字串 \(s\) 在原串中的出现位置集合。

\(endpos\) 性质。

我们有两个原串的字串 \(s, t\), \(|s| \le |t|\), \(s\) != \(t\)

  1. \(endpos(t) \in endpos(s)\), 则 \(s\)\(t\) 的后缀。

    证明 ``` 存在一个位置 $x$, 满足 $x \in endpos(s)$ 且 $x \in endpos(t)$, 则在位置 $x$ 前面我们可以找到这两个串。 ```
  2. \(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)$, 矛盾 ```
  3. \(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\) 包含这个节点。

一些关于这个树的性质

  1. 对于树上的每个节点, 表示的串长度连续且从大到小为后缀关系。可以通过反证法证明。
  2. 对于树上每个节点的最小值, 去除开头则为这个节点父亲的最长串。

我们记 \(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)\)

posted @ 2025-07-21 21:09  liuyichen  阅读(10)  评论(0)    收藏  举报