Loading

SA 与 SAM

SA 具体一些,在有些信息的处理上显得无力;SAM 较为抽象,但是方便维护很多信息。

期望受众是会打两个板子不会应用者。虽然我会对板子的理解做一些碎碎念。

基本操作

\(\rm SA\)

\(\rm SA\) 的核心是两个数组:\(sa[]\)\(height[]\)\(sa_i\) 表示以 \(i\) 开头的后缀的排名,\(height_i\) 代表 \(\text{lcp}(sa_i, sa_{i-1})\)。有结论 \(\text{lcp}(a_i, a_j) = \min_{k=i+1}^{j} height_k\)。感性理解是上后缀树,\(\text{lcp}(a_i,a_j)\) 是两点 \(\text{lca}\) 深度。

\(height\)\(\rm SA\) 的核心,它能处理一些子串相关问题。维护 \(\min\) 的常见方法是单调栈。

个人认为 \(\rm SA\) 和后缀树本质差不多。有时想问题先想后缀树再转回 \(\rm SA\) 能出奇效。

有时当需要更多信息时,可能需要 SAM。

\(\rm SAM\)

SAM Drawer (yutong.site)

给定模板串,对其所有子串定义 \(\rm endpos\) 函数,代表这个子串在原串中出现位置的右端点的集合。

显而易见的是 \(\rm endpos\) 不交或包含。把所有串按 \(\rm endpos\) 集合分为若干等价类,以每个等价类为一个节点建出 \(\rm parent \ tree\),每个节点向 \(\rm endpos\) 包含它的 \(\rm endpos\) 最小节点连边。这是一个树形结构,每个节点的 \(\rm endpos\) 都会被若干子节点划分为若干两两不交的子集合,于是可证明节点数上限 \(2n-1\),归纳法挺好证。

注意到顺着父节点往下走的过程中,\(\rm endpos\) 集合收缩,意味着串往左扩展。因此 \(\rm parent \ tree\) 上每个节点往其子节点走时都会在开头加上一些字符。显然一个串的后缀树是它的反串的 \(\rm parent \ tree\)

\(\rm endpos\) 集合可以方便地通过线段树合并维护,注意每次合并时要新建节点不能修改已有节点。把多棵线段树合并起来最多只用新开每棵树大小之和那么多的节点,因此要开 \(2 n \log n\) 的节点。

显然任意两个在等价类中的节点都有包含关系,容易证明一个等价类内的子串长度连续。因此一个节点只用储存 \(len_i\) 表示所代表等价类的最大长度。最小长度显然是其父节点的 \(len+1\)

\(\rm SAM\) 上还有一些转移边,意义就像名字一样明确。通过转移边可以检索某子串的出现次数,其它用处我不知道。可以证明转移边上限 \(3n-2\),我不会证。

这篇说得挺好,但我宁可把 SAM 当黑箱。

posted @ 2023-02-06 22:53  purplevine  阅读(136)  评论(0编辑  收藏  举报