回文树(回文自动机)(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. 一些功 ...
随机推荐
- java scoket Blocking 阻塞IO socket通信三
在NIO同步非阻塞的场景中和原来同步阻塞最大的却别就是引入了上面的Buffer对象,现在我们来学校上面的BUffer对象 我们来看看程序的代码: package bhz.nio.test; impor ...
- 设计模式--Builder生成器模式
如果文章中哪里有问题,希望各位大哥大姐指出,小弟十分感激. 正文 什么是生成器模式? 生成器模式就是把生产对象的过程进一步抽取.细化.独立.以往我们生产对象,可能就是在一个小作坊里面从头做到尾.现在用 ...
- Vue学习-组件的基本使用(局部组件)
目录 示例代码 1.创建组件(构造器对象创建-Vue.extend) 2.注册组件 3.使用组件 4.语法糖创建并注册组件 示例代码 http://jsrun.net/H8vKp/edit 1.创建组 ...
- ajax前后端交互原理(1)
1.Node.js简介 1.1.前后台数据交互流程 在web开发中,我们经常听说前端和后台,他们分别是做什么具体工作的呢?他们怎样交互的呢?我们得把这些基础的问题都搞明白了,才有一个大致的学习方向,首 ...
- Azure Monitor(二)Log Analytics
一,引言( 前情回顾) Azure Monitor 包括 Log Analytics 和 Application Insights,其提供的高级工具适用于收集和分析遥测数据,以便最大程度地提高云和本地 ...
- 【部分】@using (Html.BeginForm())参数示例
原文:https://www.cnblogs.com/firstcsharp/p/3238321.html 1.指定表单提交方式和路径等 @using (Html.BeginForm("In ...
- pandas | 使用pandas进行数据处理——Series篇
本文始发于个人公众号:TechFlow,原创不易,求个关注 上周我们关于Python中科学计算库Numpy的介绍就结束了,今天我们开始介绍一个新的常用的计算工具库,它就是大名鼎鼎的Pandas. Pa ...
- FTP学习
FTP服务的使用 一.LVM理论 介绍 FTP用于在Internet 上控制文件的双向传输. FTP的主要作用就是让用户连接一 个远程计算机(这些计算机上 运行着FTP服务器程序) ,并查看远程计算机 ...
- pycharm远程连接vagrant虚拟机中mariadb数据库
1.虚拟机数据库设置--重启数据库 (1)vi /etc/my.cnf bind = 0.0.0.0 (2).远程不能用root用户连,得新建用户 select host,user from mysq ...
- 如何配置webpack让浏览器自动补全前缀
一.postcss-loader有什么用? PostCSS 本身是一个功能比较单一的工具.它提供了一种方式用 JavaScript 代码来处理 CSS.它负责把 CSS 代码解析成抽象语法树结构(Ab ...