回文树(回文自动机)(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. 一些功 ...
随机推荐
- 【转载】提高访问 github 的速度
原文地址:https://www.cnblogs.com/liuchao888/p/11733996.html 工具地址:http://tool.chinaz.com/dns?type=1&h ...
- 入门大数据---HDFS-API
第一步:创建一个新的项目 并导入需要的jar包 公共核心包 公共依赖包 hdfs核心包 hdfs依赖包 第二步:将Linux中hadoop的配置文件拷贝到项目的src目录下 第三步:配置windows ...
- YoyoGo基于ASP.NET Core设计的Golang实现
YoyoGo YoyoGo 是一个用 Go 编写的简单,轻便,快速的 微服务框架,目前已实现了Web框架的能力,但是底层设计已支持. Github https://github.com/yoyofx/ ...
- JS判断 函数是否定义/变量是否定义
函数是否定义: <script type="text/javascript"> try { if(typeof FunName === "function&q ...
- P2114 [NOI2014]起床困难综合症【二进制运算+优化】
起床困难综合症[二进制运算+优化] 题目描述 21世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳.作为一名青春阳光好少年,atm一直坚持与起床困难综合症作斗争.通过 ...
- sql多列排序
从左到右依次排列,如果出现重复值,则按照右侧的排序规则进行排序: 例如:分数倒序排序,但是遇到重复值,则再按照class_id倒序排 例如:分数倒序排序,没有重复值,进行了正常的排序,则不再按照cla ...
- 要想数组用的 6,怎能不懂 java.util.Arrays
java.util.Arrays 类就是为数组而生的专用工具类,基本上常见的对数组的操作,Arrays 类都考虑到了,这让我由衷地觉得,是时候给该类的作者 Josh Bloch.Neal Gafter ...
- css自动省略号...,通过css实现单行、多行文本溢出显示省略号
网页开发过程中经常会遇到需要把多行文字溢出显示省略号,这篇文章将总结通过多种方法实现文本末尾省略号显示. 一.单行文本溢出显示省略号(…) 省略号在ie中可以使用text-overflow:ellip ...
- Java 添加、下载、读取PDF附件信息(基于Spire.Cloud.SDK for Java)
Spire.Cloud.SDK for Java提供了PdfAttachmentsApi接口添加附件addAttachment().下载附件downloadAttachment().获取附件信息get ...
- 重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 文无第一,武无第二 不同方向但同样努力的人,都有自身的价值和亮 ...