回文树,也叫回文自动机,是2014年被西伯利亚民族发明的,其功能如下:

1、求前缀字符串中的本质不同的回文串种类

2、求每个本质不同回文串的个数

3、以下标i为结尾的回文串个数/种类

4、每个本质不同回文串包含的本质不同回文串种类

(本文参考自Palindromic Tree——回文树【处理一类回文串问题的强力工具】,Palindromic Tree 回文自动机-回文树 解决回文串的神器)

下面介绍一些数组的意义

next[][]类似于字典树,指向当前字符串在两段同时加上一个字符

fail[] fail指针,类似于AC自动机,返回失配后与当前i结尾的最长回文串本质上不同的最长回文后缀

cnt[] 在最后统计后它可以表示形如以i为结尾的回文串中最长的那个串个数

num[] 表示以i结尾的回文串的种类数

len[] 表示以i为结尾的最长回文串长度

s[] 存放添加的字符

last 表示上一个添加的字符的位置

n 表示字符数组的第几位

p 表示树中节点的指针

在这里我想多说一下fail具体的意义,网上很多blog都说的不细致。

假设i的最长回文串为a,j的最长回文串为b,i>j,那么fail[i]=j当且仅当b为a的后缀且j是0~i-1中最长的。

以下内容转自http://blog.csdn.net/u013368721/article/details/42100363

一开始回文树有两个节点,0表示偶数长度串的根和1表示奇数长度串的根,且len[0] = 0,len[1] = -1,last = 0,S[0] = -1,n = 0,p = 2(添加了节点0、1)。

假设现在我们有串S = abbaabba。

首先我们添加第一个字符'a',S[++ n] = 'a',然后判断此时S[n - len[last] - 1]是否等于S[n],即上一个串-1的位置和新添加的位置是否相同,相同则说明构成回文。否则,last = fail[last]。此时last = 0,我们发现S[1 - 0 - 1] != S[1],所以last = fail[last] = 1,然后我们发现S[1 - (-1) - 1] == S[1](即自己等于自己,所以我们让len[1]等于-1可以让这一步更加方便)。

令cur等于此时的last(即cur = last = 1),判断此时next[cur]['a']是否已经有后继,如果next[cur]['a']没有后继,我们就进行如下的步骤:新建节点(节点数p++,且之后p = 3),并让now等于新节点的编号(now = 2),则len[now] = len[cur] + 2(每一个回文串的长度总是在其最长子回文串的基础上在两边加上两个相同的字符构成的,所以是+2,同时体现出我们让len[1] = -1的优势,一个字符自成一个奇回文串时回文串的长度为(-1) + 2 = 1)。然后我们让fail[now] = next[get_fail ( fail[cur] )]['a'],即得到fail[now](此时为fail[2] = 0),其中的get_fail函数就是让找到第一个使得S[n - len[last] - 1] == S[n]的last。然后next[cur]['a'] = now。

当上面步骤完成后我们让last = next[cur][c](不管next[cur]['a']是否有后继),然后cnt[last] ++。

此时回文树为下图状态:

现在我们添加第二个字符字符'b'到回文树中:

继续添加第三个字符'b'到回文树中:

继续添加第四个字符'a'到回文树中:

继续添加第五个字符'a'到回文树中:

继续添加第六个字符'b'到回文树中:

继续添加第七个字符'b'到回文树中:

继续添加第八个字符'a'到回文树中:

到此,串S已经完全插入到回文树中了,现在所有的数据如下:

然后我们将节点x在fail指针树中将自己的cnt累加给父亲,从叶子开始倒着加,最后就能得到串S中出现的每一个本质不同回文串的个数。

构造回文树需要的空间复杂度为O(N*字符集大小),时间复杂度为O(N*log(字符集大小)),这个时间复杂度比较神奇。如果空间需求太大,可以改成邻接表的形式存储,不过相应的要牺牲一些时间。

总的来说,这是一个很好的算法~

下面给出模板代码

 const int MAXN =  ;
const int N = ; struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针 int newnode ( int l ) {//新建节点
for ( int i = ; i < N ; ++ i ) next[p][i] = ;
cnt[p] = ;
num[p] = ;
len[p] = l ;
return p ++ ;
} void init () {//初始化
p = ;
newnode ( ) ;
newnode ( - ) ;
last = ;
n = ;
S[n] = - ;//开头放一个字符集中没有的字符,减少特判
fail[] = ;
} int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - ] != S[n] ) x = fail[x] ;
return x ;
} void add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + ;
}
last = next[cur][c] ;
cnt[last] ++ ;
} void count () {
for ( int i = p - ; i >= ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
} ;

Palindromic Tree 回文自动机-回文树 例题+讲解的更多相关文章

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

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

  2. 回文树 Palindromic Tree

    回文树 Palindromic Tree 嗯..回文树是个什么东西呢. 回文树(或者说是回文自动机)每个节点代表一个本质不同的回文串. 首先它类似字典树,每个节点有SIGMA个儿子,表示对应的字母. ...

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

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

  4. Ural 2040. Palindromes and Super Abilities 2 回文自动机

    2040. Palindromes and Super Abilities 2 题目连接: http://acm.timus.ru/problem.aspx?space=1&num=2040 ...

  5. 【XSY2715】回文串 树链剖分 回文自动机

    题目描述 有一个字符串\(s\),长度为\(n\).有\(m\)个操作: \(addl ~c\):在\(s\)左边加上一个字符\(c\) \(addr~c\):在\(s\)右边加上一个字符 \(tra ...

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

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

  7. BZOJ 3790 神奇项链(回文自动机+线段树优化DP)

    我们预处理出来以i为结尾的最长回文后缀(回文自动机的构建过程中就可以求出)然后就是一个区间覆盖,因为我懒得写贪心,就写了线段树优化的DP. #include<iostream> #incl ...

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

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

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

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

随机推荐

  1. 【Atcoder】ARC088 D - Wide Flip

    [题目]D - Wide Flip [题意]给定n个数字的01序列,要求每次翻转>=k个数字使得全0,求最大的k.n<=10^5 [算法]数学 [题解]有两个角度可以得到等价的结论: 1. ...

  2. 洛谷 Transformations 方块转换

    Description 一块N x N(1<=N<=10)正方形的黑白瓦片的图案要被转换成新的正方形图案.写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式: 1:转90度 ...

  3. 【leetcode 简单】第三十二题 买卖股票的最佳时机Ⅱ

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你必须在再次 ...

  4. 我的Mac使用笔记

    此篇记录mac使用相关的一些东西,不断更新中... 1.Mac 安装homebrew : https://brew.sh/index_zh-cn.html /usr/bin/ruby -e " ...

  5. 基于canvas的图片编辑合成器

    在我们日常的前端开发中,经常会要给服务器上传图片,但是局限很大,图片只能是已有的,假设我想把多张图片合成一张上传就需要借助图片编辑器了,但是现在我们有了canvas合成就简单多了 首先我们看图片编辑器 ...

  6. input只读属性readonly和disabled的区别

    主要区别: 参考: http://bbs.html5cn.org/forum.php?mod=viewthread&tid=84113&highlight=input http://b ...

  7. c语言学习笔记.数组.

    数组: 可以存储一个固定大小的相同类型元素的顺序集合,比如int类型的数组.float类型的数组,里面存放的数据称为“元素”. 所有的数组都是由连续的内存位置组成.最低的地址对应第一个元素,最高的地址 ...

  8. xshell5破解版下载

    http://www.pc6.com/softview/SoftView_507840.html

  9. JS 判断是否是微信浏览器 webview

    原理很简单,就是判断 ua 中是否有字段 “micromessenger" 代码如下: function isWechat () { var ua = window.navigator.us ...

  10. Java从零到企业级电商项目实战

    欢迎关注我的微信公众号:"Java面试通关手册"(坚持原创,分享各种Java学习资源,面试题,优质文章,以及企业级Java实战项目回复关键字免费领取)回复关键字:"电商项 ...