回文树(回文自动机)(PAM)
第一个能看懂的论文:国家集训队2017论文集
这是我第一个自己理解的自动机(AC自动机不懂KMP硬背,SAM看不懂一堆引理定理硬背)
参考文献:2017国家集训队论文集 回文树及其应用 翁文涛
参考博客:回文树
一些定义
(回文)子串不包括空串。
(回文)后缀不包括原串本身,如果找不到,就是空串。
定义 \(len[cur]\) 为 \(cur\) 节点的代表串长度,\(son[p][c]\) 表示一条转移路径,\(fail\) 表示失配指针。
定义 \(\Sigma\) 为字符集大小。
\(c\) 通常表示字符,\(s\) 或者 \(S\) 通常代表字符串,\(p,cur\) 代表节点。
有时我可能会将“回文串”和“回文树上代表其的节点”混用。
回文树原理及构造
回文树的结构
回文树由两棵树组成,一棵奇树,一棵偶树。
每个节点代表恰好一个回文串,每个原串的回文子串恰好有一个对应节点。
奇树上的点长度都为奇数,偶树上的点长度都为偶数。特别地,奇树根节点(通常编号为1)的长度为 -1,偶树根节点(通常编号为0)的长度为 0.
每个点都有一个 \(fail\) 指针,指向当前串的最长回文后缀。众多 \(fail\) 构成一棵以 1 为根的 \(fail\) 树,其中父亲为儿子的最长回文后缀;满足 \(len[fa] < len[son]\);并且一个回文串的所有回文后缀为从该节点到根节点 1 所经过的链上 \(len\) 为正数的回文串。
节点数和转移(边)数
可以证明,一个字符串的本质不同的回文子串数量不超过字符串长度,因此回文树节点数为 \(O(|s|)\)。证明如下:
考虑新加入一个字符 \(c\) 所新增的位置不同的回文子串:\(s[l_1, n], s[l_2, n], ...\),那么除了 \(s[l_1, n]\) 外,其它的回文子串在之前一定已经出现过了(如图)。因此加 \(c\) 新增的本质不同的回文子串一定是 \(s[1, n]\) 的最长回文后缀。

由于每个节点最多只由一个节点转移过来,因此回文树有 \(O(|S|)\) 边。\(fail\) 树上的边显然也是 \(O(|S|)\)。
构造
增量法。
显然,每次我们只需搞出当前串的最长回文后缀即可。由于当前的最长回文后缀一定是先前的一个回文后缀加一个字符,我们可以直接在先前的最长回文后缀上暴跳 \(fail\) 链,直到合法位置(\(s[i - 1 - len[p]] == s[i]\))。显然这是一定合法的,因为 \(fail~tree\) 的根节点长度为 -1,而 \(s[i - 1 - (-1)] == s[i]\) 一定成立。
然后再用类似的方法搞出当前点的 \(fail\) 指针(最长回文后缀).\(len, son\) 随便维护一下即可。
值得注意的是,每添加一个字符,最多只会改 \(fail, len, son\) 数组中的一个位置。
关键代码
int son[N][26], fail[N], lst, len[N], tot;
inline void init() {
len[1] = -1;
fail[1] = fail[0] = tot = 1;
}
inline int ins(int pos, int c) {
int p = lst;
while (s[pos - 1 - len[p]] != s[pos]) p = fail[p];
if (son[p][c]) return Len[lst = son[p][c]];
int np = ++tot, x = fail[p];
while (s[pos - 1 - len[x]] != s[pos]) x = fail[x];
fail[np] = son[x][c];
len[np] = len[p] + 2;
son[p][c] = np;
return Len[lst = np];
}
复杂度
分析一下时间复杂度。势能分析瞎搞搞就可以了。 我们死盯一个量:当前的节点在 \(fail~tree\) 上的深度。我们发现,每跳一次 \(fail\) 这个值会减一;每插入一个字符,这个值会加一。这个值始终非负,而最多加了 \(|S|\)。因此时间复杂度是 \(O(|S|)\) 的。
因此,时间 \(O(|S|)\),空间 \(O(|S|\Sigma)\)。
如果 \(\Sigma\) 比较大,可以使用 \(map\) 存储 \(son\),时间 \(O(|S|log\Sigma)\),空间 \(O(|S|)\)
拓展
支持前后加字符
与向后加字符类似,我们可以维护最长回文后缀的指针 \(fail'\),形成两棵 \(fail~tree\) 。并且我们发现一个神奇的性质:如果一个回文串 \(t\) 的最长回文后缀为 \(t[i...|t|]\),那么根据回文串的对称性,其最长回文前缀为 \(t[1...|t|-i+1]\),并且这两个回文串是一样的。也就是说, \(fail\) 指针和 \(fail'\) 指针指向的是同一个节点 !那么我们就方便很多了,只需要多维护个 \(lst\),前后插入字符的同时都维护一下 \(fail\) 指针,整棵树的形态就是对的。
唯一一点需要注意的是,我们在前面插入一个字符 \(c\),最当前串的最长回文后缀可能产生影响,当且仅当插入 \(c\) 后整个串是一个回文串(显然)。因此注意修改后缀的 \(lst\)。后面插入对前缀的影响同理。
回文树(回文自动机)(PAM)的更多相关文章
- [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串
回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ...
- 回文树(回文自动机) - URAL 1960 Palindromes and Super Abilities
Palindromes and Super Abilities Problem's Link: http://acm.timus.ru/problem.aspx?space=1&num=19 ...
- 回文树(回文自动机PAM)小结
回文树学习博客:lwfcgz poursoul 边写边更新,大概会把回文树总结在一个博客里吧... 回文树的功能 假设我们有一个串S,S下标从0开始,则回文树能做到如下几点: 1.求串S前缀0~ ...
- 回文树/回文自动机(PAM)学习笔记
回文树(也就是回文自动机)实际上是奇偶两棵树,每一个节点代表一个本质不同的回文子串(一棵树上的串长度全部是奇数,另一棵全部是偶数),原串中每一个本质不同的回文子串都在树上出现一次且仅一次. 一个节点的 ...
- 回文树(回文自动机) - BZOJ 3676 回文串
BZOJ 3676 回文串 Problem's Link: http://www.lydsy.com/JudgeOnline/problem.php?id=3676 Mean: 略 analyse: ...
- BZOJ 3676: [Apio2014]回文串 回文树 回文自动机
http://www.lydsy.com/JudgeOnline/problem.php?id=3676 另一种更简单更快常数更小的写法,很神奇……背板子. #include<iostream& ...
- 省选算法学习-回文自动机 && 回文树
前置知识 首先你得会manacher,并理解manacher为什么是对的(不用理解为什么它是$O(n)$,这个大概记住就好了,不过理解了更方便做$PAM$的题) 什么是回文自动机? 回文自动机(Pal ...
- HackerRank Special Substrings 回文树+后缀自动机+set
传送门 既然要求对每个前缀都求出答案,不难想到应该用回文树求出所有本质不同的回文子串. 然后考虑如何对这些回文子串的前缀进行去重. 结论:答案等于所有本质不同的回文子串长之和减去字典序相邻的回文子串的 ...
- 回文自动机(PAM) 入门讲解
处理回文串,Manacher算法也是很不错,但在有些问题的处理上比较麻烦,比如求本质不同的子串的数量还需要结合后缀数组才能解决.今天的们介绍一种能够方便的解决关于回文串的问题的算法--PAM. 一些功 ...
随机推荐
- RabbitMQ:四、跨越集群
跨越集群主要两种插件:Federation和Shovel. 原来的rabbitmq集群将多个broker将多个节点连接起来组成逻辑上独立的单个broker,但是集群也有其局限性:集群内部借助 Erla ...
- ⚡ vue3 全家桶体验
前置 从创建一个简单浏览器导航首页项目展开,该篇随笔包含以下内容的简单上手: vite vue3 vuex4 vue-router next 预览效果有助于理清这些内容,限于篇幅,不容易展开叙述.由于 ...
- 洛谷 P1215 【[USACO1.4]母亲的牛奶 Mother's Milk】
这道题\(DFS\)就好了,六种情况,\(ab,ac,ba,bc,ca,cb\),我们直接枚举就可.什么?这样不会结束?用一个\(vis\)数组判断走过没有就可以了.最后排序输出即可. \(code: ...
- spring boot 整合Thymeleaf模板
SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...
- springmvc json乱码问题
在方法上加上:produces属性即可 @RequestMapping(produces = "application/json;charset=utf-8")
- 《The Google File System》论文研读
GFS 论文总结 说明:本文为论文 <The Google File System> 的个人总结,难免有理解不到位之处,欢迎交流与指正 . 论文地址:GFS Paper 阅读此论文的过程中 ...
- 资深前端工程师带你认识网页后缀html、htm、shtml、shtm有什么区别?
每一个网页或者说是web页都有其固定的后缀名,不同的后缀名对应着不同的文件格式和不同的规则.协议.用法,最常见的web页的后缀名是.html和.htm,但这只是web页最基本的两种文件格式,今天我们来 ...
- python 的迭代
字典的迭代: #创建字典 dict={'a':1,'b':2,'c':3} #key和value的迭代 for key,value in dict.items(): print(key,':',val ...
- Ticket Game CodeForces - 1215D 博弈题
题目描述 Monocarp and Bicarp live in Berland, where every bus ticket consists of n digits (n is an even ...
- 从零开始学Electron笔记(二)
在之前的文章我们简单介绍了一下Electron可以用WEB语言开发桌面级应用,接下来我们继续说一下Electron的菜单创建和事件绑定. 我们接上一章的代码继续编写,上一章代码 https://www ...