换句话说:
-- SAM 是一张有向无环图。结点被称作 **状态** ,边被称作状态间的 **转移** 。
+- SAM 是一张有向无环图。结点被称作 **状态** ,边被称作状态间的 **转移** 。
- 图存在一个源点 $t_0$ ,称作 **初始状态** ,其它各结点均可从 $t_0$ 出发到达。
- 每个 **转移** 都标有一些字母。从一个结点出发的所有转移均 **不同** 。
- 存在一个或多个 **终止状态** 。如果我们从初始状态 $t_0$ 出发,最终转移到了一个终止状态,则路径上的所有转移连接起来一定是字符串 $s$ 的一个后缀。 $s$ 的每个后缀均可用一条从 $t_0$ 到某个终止状态的路径构成。
-- å\9c¨æ\89\80æ\9c\89满足ä¸\8aè¿°æ\9d¡ä»¶ç\9a\84è\87ªå\8a¨æ\9cºä¸ï¼\8c SAM ç\9a\84ç»\93ç\82¹æ\95°æ\9c\80å°\91。
+- å\9c¨æ\89\80æ\9c\89满足ä¸\8aè¿°æ\9d¡ä»¶ç\9a\84è\87ªå\8a¨æ\9cºä¸ï¼\8c SAM ç\9a\84ç»\93ç\82¹æ\95°æ\98¯æ\9c\80å°\91ç\9a\84。
### 子串的性质
- SAM 最简单、也最重要的性质是,它包含关于字符串 $s$ 的所有子串的信息。任意从初始状态 $t_0$ 开始的路径,如果我们将转移路径上的标号写下来,都会形成 $s$ 的一个 **子串** 。反之每个 $s$ 的子串对应从 $t_0$ 开始的某条路径。
+SAM 最简单、也最重要的性质是,它包含关于字符串 $s$ 的所有子串的信息。任意从初始状态 $t_0$ 开始的路径,如果我们将转移路径上的标号写下来,都会形成 $s$ 的一个 **子串** 。反之每个 $s$ 的子串对应从 $t_0$ 开始的某条路径。
为了简化表达,我们称子串 **对应** 一条路径(从 $t_0$ 开始、由一些标号构成这个子串)。反过来,我们说任意一条路径 **对应** 它的标号构成的字符串。
### 构造 SAM
-我们将会在这里展示一些简单的字符串的 SAM 。
+我们将会在这里展示一些简单的字符串的后缀自动机 。
我们用蓝色表示初始状态,用绿色表示终止状态。
## 在线性时间内构造 SAM
-在我们描述线性时间内构造 SAM 的算法之前,我们需要引入几个对理解构造过程非常重要的概念并简单证明。
+在我们描述线性时间内构造 SAM 的算法之前,我们需要引入几个对理解构造过程非常重要的概念并对其进行简单证明。
### 结束位置 $endpos$
显然, SAM 中的每个状态对应一个或多个 $endpos$ 相同的子串。换句话说, SAM 中的状态数等于所有子串的等价类的个数,再加上初始状态。 SAM 的状态个数等价于 $endpos$ 相同的一个或多个子串所组成的集合的个数 $+1$ 。
-我们稍后将会用这个假设介绍构造 SAM 的算法。我们将发现, SAM 需要满足的所有性质,除了最小性以外都满足了。由 Nerode 定理我们可以得出最小性(不会在这篇文章中证明)。
+我们稍后将会用这个假设来介绍构造 SAM 的算法。我们将发现, SAM 需要满足的所有性质,除了最小性以外都满足了。由 Nerode 定理我们可以得出最小性(不会在这篇文章中证明)。
由 $endpos$ 的值我们可以得到一些重要结论:
>
> $$
> \begin{cases}
-> endpos(w)\subseteq endpos(u)&\text{若 $u$ 为 $w$ 的一个后缀}\\
-> endpos(w)\cap endpos(u)=\varnothing&\text{另一种情况}\\
+> endpos(w)\subseteq endpos(u) & if\ u\ is\ a\ suffix\ of\ w\\
+> endpos(w)\cap endpos(u)=\varnothing & otherwise\\
> \end{cases}
> $$
我们只需要考虑两个可能有相同 $endpos$ 值的不同状态。如果一个状态是由另一个复制而来的,则这种情况会发生。然而,这并不会对复杂度分析造成影响,因为每个状态至多被复制一次。
-此外,如果我们不从被复制的节点输出位置,我们也可以去除重复的位置。事实上对于一个状态,如果经过被复制状态可以到达,则经过原状态也可以到达。因此,如果我们给每个状态记录标记 `is_cloned` ,我们就可以简单地忽略掉被复制的状态,只输出其它所有状态的 $firstpos$ 的值。
+此外,如果我们不从被复制的节点输出位置,我们也可以去除重复的位置。事实上对于一个状态,如果经过被复制状态可以到达,则经过原状态也可以到达。因此,如果我们给每个状态记录标记 `is_clone` 来代表这个状态是不是被复制出来的,我们就可以简单地忽略掉被复制的状态,只输出其它所有状态的 $firstpos$ 的值。
以下是大致的实现: