摘自 《2017 年国家集训队论文》“ 回文树及其应用 ”
-
一些规定 :Σ 表示字符集大小
串 S 和字符 c,用 Sc 表示将 c 接在 S 的后面
-
回文树:回文树是一棵由两棵树组成的森林,两棵树的根分别为 odd,even,回文树上的除根以外的一个结点表示一个字符串,每条边有一个字符,len 表示一个结点代表的字符串的长度,一个结点代表的字符串可以由如下方式得到:
从根开始沿着路径走,走过一条边就将边上的字母添加到两边,即 T→cTc,其中从 odd 出发的第一步只添加一个字符
fail 表示一个结点的失配指针,一个成型的回文树如下:

-
容易注意到,一个长为 ∣S∣ 的字符串的 PAM 结点数为 ∣S∣+2,要证明这点,我们只需要证明一个长为∣S∣ 的串的本质不同子串数为 ∣S∣,考虑数学归纳法:
∣S∣=1 时成立
∣S∣>1 时假设S=S′c,考虑 c 与前面的串形成的新的回文后缀,不妨设这些右端点为 l1,l2,…,lk,若右端点为空集,那么 c 自成回文串,否则注意到若 S[l1..∣S∣],S[lk..∣S∣] 是一个回文串的话,那么 S[l1..l1+∣S∣−lk+1] 也为一个回文串,而 l1+∣S∣−lk+1≤∣S∣−1 是之前出现过的,故一个末尾字符新产生的不同回文串只有一个,这是它的最长回文后缀
-
构造方法:
我们需要对每个串 Sc 找到它的最长回文后缀,对此,我们在 S 代表的串的最长后缀的 fail 链上找一个最长的串 T,满足 S[∣S∣−lent]=c,那么 Sc 代表的回文串为 cTc,我们新建一个结点表示这个点后,还需要求出它的 fail,这一步相当于在 failT 的链上求一个最长的串 v 满足 S[∣S∣−lenv]=c
-
复杂度分析:空间复杂度 O(∣S∣),时间复杂度 O(∣S∣logΣ)
考虑势能分析跳 fail 树的这一步,一是找最长回文后缀的贡献,一是找 fail 的贡献,二者是同阶的,我们分析前者,假设最长回文后缀为 leni,那么有 leni+1≤leni+1,故只会增长 n 次,所以一共会跳至多 n 步 fail,在 fail 处检查 c 是否存在,花费 logΣ 的代价
当然也可以变成 O(∣S∣Σ)+O(∣S∣)
-
∣right∣ 集合,即 fail 树中子树右端点的点集,性质与 SAM 一样,表示出现次数
-
前端插入:
对此,我们需要维护 fail′ 来寻找最长回文前缀,而注意到每个串都是回文串的性质,发现有 fail′=fail 的性质
-
不基于势能分析的插入方法:
我们对一个结点 t 维护 quick(c) 表示 t 最长的满足前驱为 c 的回文后缀
注意到 t 的 quick 和 failt 的 quick 没有什么区别,我们将 failt 的 quick 复制给 t
令 c 为 failt 的前驱,后用 failt 来更新 t 的 quick(c) 即可
在 trie 树上建 PAM 就需要不基于势能分析的插入方法
相关文章: