首页 > 代码库 > [补档计划] SAM

[补档计划] SAM

  对于一个算法或者数据结构的学习, 我们首先要弄清它的概念, 然后理解它的构建, 进而是实现和复杂度分析, 最后考虑如何应用它.

  现在学习的是 SAM, Suffix Automaton, 后缀自动机.

  推荐陈立杰的冬令营讲稿. https://wenku.baidu.com/view/90f22eec551810a6f4248606.html

什么是自动机?

  有限状态自动机的功能是识别字符串. 令一个自动机 $A$ , 若 $A$ 能识别字符串 $S$ , 则 $A(S) = True$ ; 若不能识别字符串 $S$ , 则 $A(S) = False$ . 特别地, 后缀自动机能识别一个字符串的所有后缀. 例如, 令 $S = ababb$ , 对 $S$ 构建后缀自动机 $A$ , 则 $A(bb) = True$, $A(abb) = True$, $A(baa) = False$.

  如何用一些量刻画一个自动机? 我们需要用一个五元组: 字符集, 状态集, 初始状态, 结束状态, 转移函数.

  自动机 $A$ 能识别的字符串 $S$ , 要满足 $trans(A, S)\in 结束状态$ , 记作 $Reg(A)$ . 从自动机 $A$ 的某个状态 $s$ 作为起始节点, 能识别的字符串 $S$ , 要满足 $trans(s, S)\in 结束状态$ , 记作 $Reg(s)$ .

后缀自动机的定义

  对于字符串 $S$ , 对应后缀自动机 SAM .

  SAM 是能识别字符串 $S$ 的所有后缀的自动机. 即 $SAM(s) = True$, 当且仅当 $s$ 是 $S$ 的后缀.

  同时在后面我们能看出, SAM 能识别字符串 $S$ 的所有子串.

最简单的实现

  将每个后缀插入 Trie树 中. 状态集为 Trie树 的 $V$ , 起始状态为 Trie树 的 Root, 结束状态为 Trie树 的叶子节点, 转移函数为 Trie树 的边.

  时间复杂度和空间复杂度均为 $O(n^2)$ . 这可能很不能接受.

  考虑进行优化, 减少 SAM 的状态数.

最简状态后缀自动机

  顾名思义, 就是状态数最少的 SAM, 后面我们可以证明状态数为 $O(n)$ .

  我们需要先研究它的性质, 对它充分理解并应用, 基于此才能构建出 最简状态SAM .

 

  为了研究的方便, 我们设定记号 $ST(str) = trans(init, str)$ , 即从开始状态开始读入字符串 str 之后, 能到达的状态.

  令母串为 $S$ , 它的后缀集合为 $Suf$ , 连续子串集合为 $Fac$ .

  从位置 $a$ 开始的后缀为 $Suffix(a)$.

  $S[l,r)$ 表示 $S[l : r-1]$, 下标从 $0$ 开始.

 

  对于一个字符串 $s$, 若 $s\in Fac$ , 则 $ST(s)\ne null$ . 因为不能放过可能识别到后缀的可能性.

  对于一个字符串 $s$, 若 $s\not\in Fac$ , 则 $ST(s) = null$ . 因为既然不是子串, 那么也不可能识别到后缀, 我们要最简状态数.

  

  对于 $ST(s)$ , 假设 $x\in Reg(ST(s))$ , 则 $sx\in Reg(A)$ , 所以 $sx$ 为 $S$ 的后缀, 所以 $x$ 为 $S$ 的后缀.

  所以对于一个状态 $a$ , $Reg(a)$ 为 $S$ 的某些后缀的集合.

  更具体地, 如果 $s$ 在 $[l,r)$ 出现过, 那么 $Suffix(r)\in Reg(ST(s))$ .

  记 $Right(s) = \left\{ r_1,r_2,...,r_n \right\}$ , 则 $Reg(ST(s))$ 由 $Right(s)$ 决定.

  

[补档计划] SAM