0.什么是Manacher
Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 O(n) 的时间处理出以字符串中每一个字符为中心的回文串半径——摘自百度百科
1.从暴力谈起
先看一道例题洛谷P3805
题意十分简单明了,求一个字符串$S$中的最大回文子串长度
很容易想到一种$O(N^3)$算法,枚举子串的左边界$l$右边界$r$,判断该子串是否回文即可。
1 | for(int l = 0; l < len; l ++) |
在此算法上加入亿一点简单优化可以优化到$O(N^2)$,对于长度为奇数的回文字符串,回文子串的中心一定是某个字符,对于长度为偶数的回文字符串,回文子串的中心一定是两个字符之间的空隙,一种显然的作法是枚举这些回文中心,向两边拓展直到字符不相等或到达边界
由于$WKAHPM$比较懒,所以就不贴代码了。
暴力的缺点:
- 1.需要分类讨论
- 2.枚举了很多没用的子串
这时候我们就需要再加入亿一点优化。
2.Manacher的初始化
暴力的缺点——需要分类讨论
如何解决这个问题?一种作法是在每个字符之间再加入一个分隔字符(当然这个字符不能存在在原本的字符串中),举个栗子,比如字符串$ababac$:
a | b | a | b | a | c | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
# | a | # | b | # | a | # | b | # | a | # | c | # |
这个方法有效地避免了分类讨论,容易发现我们插入了$N+1$个#号,字符串的长度变为$2N+1$,为奇数,是不是很神奇呢
为了防止越界情况,我们在开头和结尾再加入一个字符,比如$@$,这部分的代码可以这么写
1 | void news() |
3.Manacher的原理
定义$p[i]$表示以$i$为对称中心能扩展的最大回文子串半径,即左/右边界到对称中心的距离。
$Manacher$的算法思想就是快速求出$p$数组。
我们来看看$p$数组与答案的关系(这里省略@)
s | # | a | # | b | # | a | # | b | # | a | # | c | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ans | 0 | 1 | 0 | 3 | 0 | 5 | 0 | 3 | 0 | 1 | 0 | 1 | 0 |
p | 1 | 2 | 1 | 4 | 1 | 6 | 1 | 4 | 1 | 2 | 1 | 2 | 1 |
显然有$ans[i]=p[i]-1$
证明:
设回文子串$s$原来的长度为$len$,对称中心为$i$;
则$s$插入字符后的长度变为$2\times len+1$;
此时$p[i]=(2\times len+1+1)/2=len+1$,所以$p[i]-1$即为答案
所以求出$p[i]$就可以求出答案
4.Manacher的实现
$Manacher$的实现用到了回文子串的一个性质——对称性
利用该性质可以有效地避免重复枚举,通过之前枚举过的子串来确定这个点至少能扩展的长度
设$mr$为之前找到的回文子串的最右边,$mid$为该回文子串的对称中心。
假设当前枚举到第$i$位,因为枚举过$mid$了,所以显然有$i>mid$,接下来需要分类讨论两种情况来快速求出$p[i]$:
为了方便讨论,设$j$为$i$以$mid$为对称中心的对称点,$s_{i}$是以$i$为对称中心的回文子串,$s_{j}$是以$j$为对称中心的回文子串
(1)i < mr
这时候我们又能分2种情况
- 1 $s_{i}$的右边界不大于$mr$
如图
由对称性知,此时$p[i]=p[j]$
- 2 $s_{i}$的右边界大于$mr$
如图
这时我们只能确定α段是具有对称性的,此时$p[i]=mr-i$
也就是说,当$i$在$mr$左边,那么$p[i]$肯定是$mr-i$与$p[j]$的最小值。因为$j$与$i$关于$mid$对称,所以在$[i,mr]$这一段内可以直接使用$p[j]$的值,但是$i$已知的回文串长度不能到$mr$后面去,所以跟$mr-i$取$min$。
(2) i >= mr
此时我们无法通过对称性得到$p[i]$,所以$p[i]=1$
代码实现如下:
1 | int Manacher() |
5.例题
这题可以用$Manacher$来解决。
可以先用中序遍历对每个节点编号,利用$a$和$size$数组存放每个节点的权值和以这个节点为根节点的二叉树的节点数,对每个数组进行$Manacher$,将先后两次的以第$i$个点为对称中心的最大回文子串半径存放在$p1[i],p2[i]$中。
另外本题不需要初始化,$Manacher$初始化的原因是可能存在偶数长度的回文子串,但在对称二叉树中不可能存在偶数长度的回文子串,因为在对称二叉树中,除根节点以外每一个左儿子都有一个右儿子与之对应,所以节点数为奇数个。
求出$p1,p2$后,枚举编号$1$~$N$,判断以$i$为根节点的二叉树是否为对称二叉树,如果是则对$ans$和$size[i]$取$max$,对称二叉树需要满足两个条件:
1 权值对称
2 结构对称
体现在程序中就是$p1[i] \times 2 - 1 >= size[i]$并且$p2[i] \times 2 - 1 == size[i]$
首先解释一下为什么是$p1[i] \times 2 - 1$而不是$p1[i]-1$
$p1[i] \times 2 - 1$是因为之前的$Manacher$我们没有进行初始化,所以此时的$p1[i]$要乘2
接着解释一下为什么是$p1[i] \times 2 - 1 >= size[i]$
如图
如果写成$p1[i] \times 2 - 1 == size[i]$的话,则对于编号为$5$的节点,$p1[5] \ times 2 - 1 =5$,而$size[5]=3$,所以当节点权值相等时,可能出现$p1[i] \times 2 - 1 > size[i]$的情况。
6.结语
明天就是$CSP$了,大家调整好心态,多背背模板,基本上$t1t2$过了$t3t4$打一下暴力就省一了,祝各位$RP++$(这不是毒奶)
愿降临到你身边