O(n)时间求 最长回文子串 Manacher算法
- 回文字符串分为奇回文和偶回文,在字符串中间插入任意字符使得串变成奇回文串
暴力思想:肯定是找一个点往两边任意扩展,遍历一次,
Manacher时间为O(n)
- 开一个数组
p[]
记录 以点i
为中点 的最长回文串的半径, - 假设前
i-1
个点的p[]
都已经求出来来,现在考虑p[i]
如何推导
重点:
3. R
是p[1]~p[i-1]
中推出来的右端最远最长回文子串的右端点,且用pos
记录下来,每一步更新pos+R
,既然R
知道了,L
也知道(没啥意义)
4. 当前遍历到i
点,j
是与i
对称的点,且 $j=pos-(i-pos)$ ->
$j=2pos-i$,我们可以通过j
点来得到i
点的p[i]
值
5. 此时i j R
有三种情况
a. 第一个图中红色都在[L,R]
范围内,R-i>p[j]
,直接得到p[i]
的值,p[i]=p[j]
b.
此时j
点的回文子串只有一部分在[L,R]
(最大的回文子串)中,那么超出范围的肯定不符合要求,R-i<p[j]
所以p[i]=R-i
c. 剩下一个情况应该都看出来了,就是i>=R
,现在只能往两边暴力扩展了
总结步骤:
- 先更新
p[]
,有三种情况 - 向两边暴力扩展回文字符串,满足前两种情况这一步肯定扩展不出来新的回文串,所以不需要特殊判断是否
if(p[i]==1)
,所以这一步往两边扩展 - 更新
[L,R]
右端最远的回文子串,并且记录pos
的值, - 这是(插入字符的奇数字符串)上的一系列操作,我们要转化成源字符串上的位置与长度 这里是2个结论,
自己对比就可以知道了,
原字符串的位置:index = (resid - reslen)/2
源字符串的长度:len = reslen-1
- 在第一个添加一个与其他都不同的字符,就是为了
index
正确求出来
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!