摘自 《2017 年国家集训队论文》“ 回文树及其应用 ”

  • 一些规定 :Σ\Sigma 表示字符集大小
    SS 和字符 cc,用 ScSc 表示将 cc 接在 SS 的后面

  • 回文树:回文树是一棵由两棵树组成的森林,两棵树的根分别为 odd,evenodd,even,回文树上的除根以外的一个结点表示一个字符串,每条边有一个字符,lenlen 表示一个结点代表的字符串的长度,一个结点代表的字符串可以由如下方式得到:
    从根开始沿着路径走,走过一条边就将边上的字母添加到两边,即 TcTcT\to cTc,其中从 oddodd 出发的第一步只添加一个字符
    failfail 表示一个结点的失配指针,一个成型的回文树如下:
    回文树 PAM 学习笔记

  • 容易注意到,一个长为 S|S| 的字符串的 PAMPAM 结点数为 S+2|S|+2,要证明这点,我们只需要证明一个长为S|S| 的串的本质不同子串数为 S|S|,考虑数学归纳法:
    S=1|S|=1 时成立
    S>1|S|>1 时假设S=ScS=S'c,考虑 cc 与前面的串形成的新的回文后缀,不妨设这些右端点为 l1,l2,,lkl_1,l_2,\dots,l_k,若右端点为空集,那么 cc 自成回文串,否则注意到若 S[l1..S],S[lk..S]S[l_1..|S|],S[l_k..|S|] 是一个回文串的话,那么 S[l1..l1+Slk+1]S[l_1..l_1+|S|-l_k+1] 也为一个回文串,而 l1+Slk+1S1l_1+|S|-l_k+1\le |S|-1 是之前出现过的,故一个末尾字符新产生的不同回文串只有一个,这是它的最长回文后缀

  • 构造方法:
    我们需要对每个串 ScSc 找到它的最长回文后缀,对此,我们在 SS 代表的串的最长后缀的 failfail 链上找一个最长的串 TT,满足 S[Slent]=cS[|S|-len_t]=c,那么 ScSc 代表的回文串为 cTccTc,我们新建一个结点表示这个点后,还需要求出它的 failfail,这一步相当于在 failTfail_T 的链上求一个最长的串 vv 满足 S[Slenv]=cS[|S|-len_v]=c

  • 复杂度分析:空间复杂度 O(S)O(|S|),时间复杂度 O(SlogΣ)O(|S|\log \Sigma)
    考虑势能分析跳 failfail 树的这一步,一是找最长回文后缀的贡献,一是找 failfail 的贡献,二者是同阶的,我们分析前者,假设最长回文后缀为 lenilen_i,那么有 leni+1leni+1len_{i+1}\le len_i+1,故只会增长 nn 次,所以一共会跳至多 nnfailfail,在 failfail 处检查 cc 是否存在,花费 logΣ\log \Sigma 的代价
    当然也可以变成 O(SΣ)+O(S)O(|S|\Sigma)+O(|S|)

  • right|right| 集合,即 failfail 树中子树右端点的点集,性质与 SAMSAM 一样,表示出现次数

  • 前端插入:
    对此,我们需要维护 failfail' 来寻找最长回文前缀,而注意到每个串都是回文串的性质,发现有 fail=failfail'=fail 的性质

  • 不基于势能分析的插入方法:
    我们对一个结点 tt 维护 quick(c)quick(c) 表示 tt 最长的满足前驱为 cc 的回文后缀
    注意到 ttquickquickfailtfail_tquickquick 没有什么区别,我们将 failtfail_tquickquick 复制给 tt
    ccfailtfail_t 的前驱,后用 failtfail_t 来更新 ttquick(c)quick(c) 即可
    trietrie 树上建 PAMPAM 就需要不基于势能分析的插入方法

相关文章: