第一个能看懂的论文:国家集训队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|)\)

拓展

支持前后加字符

题目:hdu 5421 Victor and String

与向后加字符类似,我们可以维护最长回文后缀的指针 \(fail'\),形成两棵 \(fail~tree\) 。并且我们发现一个神奇的性质:如果一个回文串 \(t\) 的最长回文后缀为 \(t[i...|t|]\),那么根据回文串的对称性,其最长回文前缀为 \(t[1...|t|-i+1]\),并且这两个回文串是一样的。也就是说, \(fail\) 指针和 \(fail'\) 指针指向的是同一个节点 !那么我们就方便很多了,只需要多维护个 \(lst\),前后插入字符的同时都维护一下 \(fail\) 指针,整棵树的形态就是对的。

唯一一点需要注意的是,我们在前面插入一个字符 \(c\),最当前串的最长回文后缀可能产生影响,当且仅当插入 \(c\) 后整个串是一个回文串(显然)。因此注意修改后缀的 \(lst\)。后面插入对前缀的影响同理。

回文树(回文自动机)(PAM)的更多相关文章

  1. [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串

    回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ...

  2. 回文树(回文自动机) - URAL 1960 Palindromes and Super Abilities

     Palindromes and Super Abilities Problem's Link: http://acm.timus.ru/problem.aspx?space=1&num=19 ...

  3. 回文树(回文自动机PAM)小结

    回文树学习博客:lwfcgz    poursoul 边写边更新,大概会把回文树总结在一个博客里吧... 回文树的功能 假设我们有一个串S,S下标从0开始,则回文树能做到如下几点: 1.求串S前缀0~ ...

  4. 回文树/回文自动机(PAM)学习笔记

    回文树(也就是回文自动机)实际上是奇偶两棵树,每一个节点代表一个本质不同的回文子串(一棵树上的串长度全部是奇数,另一棵全部是偶数),原串中每一个本质不同的回文子串都在树上出现一次且仅一次. 一个节点的 ...

  5. 回文树(回文自动机) - BZOJ 3676 回文串

    BZOJ 3676 回文串 Problem's Link: http://www.lydsy.com/JudgeOnline/problem.php?id=3676 Mean: 略 analyse: ...

  6. BZOJ 3676: [Apio2014]回文串 回文树 回文自动机

    http://www.lydsy.com/JudgeOnline/problem.php?id=3676 另一种更简单更快常数更小的写法,很神奇……背板子. #include<iostream& ...

  7. 省选算法学习-回文自动机 && 回文树

    前置知识 首先你得会manacher,并理解manacher为什么是对的(不用理解为什么它是$O(n)$,这个大概记住就好了,不过理解了更方便做$PAM$的题) 什么是回文自动机? 回文自动机(Pal ...

  8. HackerRank Special Substrings 回文树+后缀自动机+set

    传送门 既然要求对每个前缀都求出答案,不难想到应该用回文树求出所有本质不同的回文子串. 然后考虑如何对这些回文子串的前缀进行去重. 结论:答案等于所有本质不同的回文子串长之和减去字典序相邻的回文子串的 ...

  9. 回文自动机(PAM) 入门讲解

    处理回文串,Manacher算法也是很不错,但在有些问题的处理上比较麻烦,比如求本质不同的子串的数量还需要结合后缀数组才能解决.今天的们介绍一种能够方便的解决关于回文串的问题的算法--PAM. 一些功 ...

随机推荐

  1. 【解读】TCP粘包拆包

    一.TCP粘包.拆包图解 假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况: 1)服务端分两次读取到了两个独立的数据包,分别是D1和D2, ...

  2. JavaWeb网上图书商城完整项目--day02-3.regist页面输入框失去焦点进行校验

    当输入框输入数据之后,当输入框失去焦点的时候,我们需要对输入的数据进行校验 l  用户名校验: 用户名不能为空: 用户名长度必须在3 ~ 20之间: 用户名已被注册(需要异步访问服务器). l  登录 ...

  3. 06.DBUnit实际运用

    在上面的代码中 package com.fjnu.service; import java.io.FileWriter; import java.sql.SQLException; import st ...

  4. SpringBoot--使用Mybatis分页插件

    1.导入分页插件包和jpa包 <dependency> <groupId>org.springframework.boot</groupId> <artifa ...

  5. QUIC/HTTP3 协议简析

    从 HTTP 的进化历史讲起,细说使用协议的变迁,了解原因发现问题,解码 QUIC 在 HTTP3 中的支撑作用,共同探讨 HTTP3 的未来. HTTP.HTTP2 和 HTTP3 先和大家来回顾一 ...

  6. 使用MWeb进行博客发布测试

    MWeb 是专业的 Markdown 写作.记笔记.静态博客生成软件,目前已支持 Mac,iPad 和 iPhone.MWeb 有以下特色: 软件本身: 使用原生的 macOS 技术打造,追求与系统的 ...

  7. Linux超强截图工具flameshot

    Pop!_OS自带的截屏快捷键如下 但讲道理这个是真的不好用 所以我们借助第三方的截图工具,这里推荐flameshot(火焰截图) 在终端键入以下命令即可安装 sudo apt update sudo ...

  8. display:inline-block 什么时候不会显示间隙?

    移除空格 使用margin负值 使用font-size:0 letter-spacing word-spacing

  9. iview国际化问题(iview官方提供的兼容vue-i18n@6.x+使用组件报错)

    问题描述: 按照iview官方的说法配置i18n发现在使用组件的时候会报错. 兼容 vue-i18n@6.x+的配置如下图 报错如下图 解决方法: 经过参考element-ui的国际化配置终于解决问题 ...

  10. scrapy 基础组件专题(三):爬虫中间件

    一.爬虫中间件简介 图 1-1 图 1-2 开始这一张之前需要先梳理一下这张图, 需要明白下载器中间件和爬虫中间件所在的位置 下载器中间件是在引擎(ENGINE)将请求推送给下载器(DOWNLOADE ...