字符串:SAM后缀排序
用 SAM 实现后缀数组
- 前言:
字符串东西太多了,想单独拉个合集出来记录一下。
基本上是应用,应该不会有什么模板的讲解,因为我也讲不明白(哎学得一点也不扎实)。
最长公共后缀 LCS
对两个字符串求最长公共后缀,那就是两节点在 \(\operatorname{parent~tree}\) 上的 \(\operatorname{len(LCA)}\) 。
正序构建 SAM ,其叶子节点表示的是一个前缀,若我们从后向前比较两个前缀的字典序,那第一个不同的地方就是其 LCS 的下一个字符。
后缀排序
用 SAM 也能在线性时间实现后缀排序,但常数较大,且不用哈希实现转移空间复杂度就是 \(O(nC)\) (\(C\) 是字符集大小),无法完美替代 SA ,但除了模板题,出题人不会这么卡,吧......
我们发现正序建立 SAM 做的是从后往前的前缀排序,因此我们逆向插入字符建 SAM 。
这样就可以求两个后缀的最长公共前缀 LCP ,就能比较出两个后缀的字典序大小。具体的,在 \(\operatorname{parent~tree}\) 上,我们确定字典序最小的搜索顺序,叶子节点的访问顺序就是后缀排序。
考虑 \(\operatorname{parent~tree}\) 上的一个节点,我们需要确定其儿子的搜索顺序,根据上文,两个儿子的最长公共后缀就是祖先的 \(\operatorname{len}\) ,我们只需要任意记录一个 \(\operatorname{endpos}\) ,就能把两个儿子代表的子串的第一个不同字符给找出来,也就是 \(s_{\operatorname{endpos-len(link(u))}}\) 。显然按照这个字符从小到大去搜索就可以,对于一个点,先搜索该值最小的点。
注意串是反向插入的,第 \(i\) 个后缀是反向串的 \(n+1-i\) 个前缀。
代码 (LOJ上的,洛谷的被卡空间)
细节
我的一些思考,可以跳过。
怎么找到一个 endpos,以及在复制的时候如何分配
对于叶子节点,当前字符串位置就是其唯一的 \(\operatorname{endpos}\),且在 \(\operatorname{parent~tree}\) 上其祖先都含有该位置,因此在复制节点的时候,可以直接把这个位置给 \(\operatorname{endpos(clone)}\)
如何线性,方便的把儿子访问顺序排出来
用基数排序,用邻接表就顺序插入,链式前向星就逆序插入。

浙公网安备 33010602011771号