2026.2.2 SAM 的构建

首先感谢 Exber。

定义

SAM 是一个 DAG,其满足从起点到任意一个终止节点,路径上的边组成的字符串都是原字符串的一个后缀。所以只需要从起点出发到某一个状态,就可以匹配原字符串的任意一个子串。

endpos

定义初始节点为 \(t_0\)

定义 \(\mathrm{endpos}(s)\) 表示字符串 \(s\) 在原串中所有出现的末尾位置构成的集合。

SAM 上的每一个点都代表了一种 endpos 的等价类,其中 \(x\) 是 SAM 上的点。\(f(x)\) 为点 \(x\) 代表的 endpos 集合。

定义 \(E(x)\) 表示 \(\{t|f(x)=\mathrm{endpos}(t)\}\)

定义 \(\mathrm{longest}(x)\) 表示点 \(x\) 代表的等价类中最长的串。也就是 \(\max\{|s|,s\in E(x)\}\)。额外定义 \(\mathrm{len}(x)=|\mathrm{longest}(x)|\)

parent

定义 \(\mathrm{parent}(x)\) 为字符串 \(\mathrm{longest}(x)\) 的一个最长的后缀,满足其所属的等价类不是 \(x\),那么这个等价类就是 \(\mathrm{parent}(x)\)。这个指针构成了一棵树。

对 parent 的解释:类似失配,随着当前 \(\mathrm{longest}(x)\) 的左字符不断被删去,endpos 集合可能会扩大,导致等价类出现第一次“改变”。而后面的改变在第一次改变后,所以跳 \(x\)\(t_0\) 的树链会枚举到所有的“改变”,也相当于枚举到了所有的后缀。

线性构造(一)

考虑当前全串 \(s\) 所在的等价类 \(nw\)。代表的 endpos 为 \(\{|s|\}\)

现在转移 \(s+c\to s'\)。其中 \(c\) 是新加入的字符。

那么一定会出现一个新等价类 \(cur\),代表的 endpos 为 \(\{|s|+1\}\)

首先 \(nw\) 有一条连向 \(cur\),边权为 \(c\) 的边。接下来我们尝试去考虑所有 \(|s|\in f(y)\) 的点 \(y\),也就是 endpos 集合包含了串尾的点。这样的点 \(y\) 可以通过跳 \(nw\) 的 parent 指针找到。

  • \(y\) 没有边权为 \(c\) 的边,则可以连一条 \(c\) 的边给 \(cur\)。这意味着这些 endpos 包含了串尾的点可以转移到新点。
  • 那如果 \(y\) 有边权为 \(c\) 的边呢?先处理一下这个点吧。

线性构造(二)

现在,全串有一个后缀 \(s_1\),其在一个等价类 \(p\) 中是最长串,且其有边权为 \(c\) 转移的边。

这意味着这个后缀在原串出现过多次,并且有一次出现的下一个字符恰好就是 \(c\)。记这个等价类对应的点为 \(p\),有边 \(p\to q\),且边权为 \(c\)

如果 \(\mathrm{len}(q)=\mathrm{len}(p)+1\),这意味着 \(\mathrm{longest}(q)=s_1+c\)。此时 \(q\) 加入了 \(c\) 的影响后还是一个等价类(只不过增加了新的位置 \(|s|+1\)),只需要令 \(\mathrm{parent}(cur)=q\)

否则呢?

此时,我们的 \(q\) 对应的串会显著的分成两类:

  • \(s_1'+c\) 一类,其中 \(s'_1+c\)\(s_1+c\) 同等价类的某些后缀。其出现的位置集合 endpos 可以包含新的位置 \(|s|+1\)
  • \(s_2+s_1+c\) 一类,其中由于 \(s_2\) 无法在全串的后缀继续匹配(可以用反证法证明),所以无法再包含新的位置 \(|s|+1\)

此时我们需要分裂 \(q\) 变为 \(q\)\(q_1\)\(q_1\) 是接纳了 \(|s|+1\) 的新状态。

\(q_1\) 的连边与 \(\mathrm{parent}\) 都不变。\(\mathrm{len}(q_1)=\mathrm{len}(p)+1\),且 \(\mathrm{parent}(cur)=q_1\)

接下来处理祖先。对于 \(p\)\(p\) 的祖先 \(u\),暴力跳找到所有 \(u\)\(c\) 的转移到了 \(q\),现在 \(q\) 太长了无法接纳新的 \(|s|+1\),所以这个转移其实属于 \(q_1\),如果发现这个条件不满足则终止跳跃就好。显然更上面的祖先对构建已经没有任何影响。

于是我们完成了 SAM 的构建。另外,如果找到 \(t_0\) 都没有边权为 \(c\) 的边,则直接令 \(\mathrm{parent}(cur)=0\) 就好了。

posted @ 2026-02-02 19:03  ty_mxzhn  阅读(0)  评论(0)    收藏  举报