Manacher算法
解决的问题
字符串中最长回文子串的长度如何求解?
如何做到时间复杂度O(n)
完成?
例如:abc1 232 0del 最长回文子串为232。
经典解法
将每个字符从中间开始向两边扩展可以得到最长回文子串吗?不能。
因为只能得到奇数的回文子串。 例如:1221。
上面只处理了实轴,我们加入虚轴。将字符串中每个字符中间加入特殊符号,如#,得到扩充字符,再使用上面的解法就可以得到答案。
例如:
将1221扩充为: #1#2#2#1,从每个字符向两边扩展,得到每个字符的最长回文串,最中间#的为9,除2,得到最长回文子串为4。
但是,加入的字符必须是没有出现的字符吗?不需要,每次处理字符串都是实轴和实轴比,虚轴和虚轴比,1和1比,#和#比。
经典解法的时间复杂度O(n)
Manacher解法
概念
回文直径和回文半径,回文直径是一个位置向2边扩的长度叫回文直径,一半是回文半径。
回文串的最右边界:,只要突破边界,就记录突破的位置。#1#2#2#1 对于第1个字符1来说是0-2,就是2;对于对于第3个字符2来说是2-4就更新为4;对于第4个字符#是0-8。
取得更远边界时中心点:对于第1个字符1来说是0-2,中心点是1;对于对于的3个字符2来说是2-4,中心点就是3;对于第4个字符#是0-8,中心点为4。
原理
解法
暴力扩,如果
i
位置没有在最右回文右边界只能暴力计算。如果
i
位置在最右回文右边界,中心点C
在i
的左侧。i_mirror
的回文区域与i位置的回文区域成逆序关系。设C的回文半径为[L-R]
。i_mirror
位置的回文半径在[L-R]
中,P[i] = P[i_mirror]
i_mirror
位置的回文半径超出[L-R]
:P[i_mirror] = 7,
但是我们发现此时P [ i ]
并不等于 7 ,为什么呢,因为我们从i
开始往后数 7 个,等于 22 ,已经超过了最右的 R ,此时不能利用对称性了,但我们一定可以扩展到 R 的,所以P [ i ]
至少等于R - i = 20 - 15 = 5
,会不会更大呢,我们只需要比较T [ R+1 ]
和T [ R+1 ]
关于i
的对称点就行了,就像中心扩展法一样一个个扩展。P [ i_mirror ]
遇到了原字符串的左边界此时
P [ i_mirror ] = 1
,但是P [ i ]
赋值成 1 是不正确的,出现这种情况的原因是P [ i_mirror ]
在扩展的时候首先是 “#” == “#” ,之后遇到了 “^”和另一个字符比较,也就是到了边界,才终止循环的。而P [ i ]
并没有遇到边界,所以我们可以继续通过中心扩展法一步一步向两边扩展就行了。
代码
1 | public class Manacher { |
参考: