Manacher 算法个人总结(精简)

求解最长回文子串必用算法:Manacher 算法。

这里不解释啥是回文子串了,直接总结下算法思路。

第一步:将原字符串首尾以及字符串之间添加'#'字符,目的是原字符串回文子串的中心点可能有两种,奇数长度和偶数长度。例如aba和abba,正常求解需要分情况讨论,所以在字符串之间加上没出现过的字符例如'#',那么都会变成奇数长度,不用分情况讨论。#a#b#a#(7)#a#b#b#a#(9),'#'长度永远比字符长度大1。用S指代构建的字符串

第二步,构建与S长度相同的数组P,P的每一位存储字符串中对应该位的最大回文长度半径。

第三步,初始化P以及最大回文的中心位置C以及最右边位置R。R是当前最大回文子串最右边的坐标,马拉车算法的精髓就是利用了回文串的对称性,所以在更新回文长度数组P的时候,要区别当前位置i是在回文子串内还是不在,至于两种情况有何不同看下面。

第一步没问题,第二步初始化过程。

例如字符串fabccbad,经过第一步得到

#f#a#b#c#c#b#a#d#.

初始化P[0]=0,P[1]=1,因为P[0]是开头,P[1]第一个字符最大回文肯定是它自己,长度为1。此时中心点位置就是1,C=1。R=i+P[i],当前是R=1+1=2

此时从下标2开始循环,

首先区别当前下标i是否在R内:

if i>R:直接使用中心扩展法,也就是以i为中心点,用前后指针往前后遍历找最长回文子串,然后用新的R和C来更新原来的R,C。

if i<=R:i在R内,先得到i对于当前回文串中心点C的镜像对应点i_mir=2*C-i。P[i]=P[i_mir]。但是这里注意边界情况。

第一种,i_mir如果正好在左边界处,也就是当前字符的镜像字符正好是字符串的头部,那P[i]不应该等于1,因为这个1是因为边界,我们人为初始化出来的,此时P[i]仍然需要用中心扩展法来确定P[i],并且用更大的R来更新R。

第二种,i+P[i_mir]>R。也就是当前坐标i虽然在回文串内,但是i+P[i_mir],它的右边界越过了R,此时P[i]只能等于R-i,接着在这基础上用中心扩展求长度,此时指针应该基于半径R-i的基础上遍历。

如果没有边界问题,P[i]=P[i_mir]。

所以对于

     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

     # f # a # b # c # c # b # a # d #

P: 0 1 0 1 0 1 0 1 6 1 0 1 0 1 0 1 0

在c#c中间的#时,R,C更新为14(i+P[i]),8(i),后面的字符都在R内,且没有边界问题,直接更新。

算法看着和中心扩展类似,但是每次我们的i超出R的时候,总会更新新的R,所以元素最多遍历两次,(更新新的R时一次,出现边界问题使用中心扩展时一次)。所以算法复杂度仍然是线性O(N)。

在最后得到的P数组里,找到元素值最大的下标i。(i-P[i])/2就是原字符串回文子串的开始下标,P[i]就是长度。

贴个python代码:

s='#b#b#'
def malache(s):
    P=[0]*len(s)
    P[1]=1
    C=1
    R=C+P[C]
    for i in range(2,len(s)):
        if i==R+1 or 2*C-i<=2:
            P[i]=0
            t1=i-1
            t2=i+1
            while t1>=0 and t2<len(s) and s[t1]==s[t2]:
                P[i]+=1
                t1-=1
                t2+=1
        elif i+P[2*C-i]>=R:
            P[i]=R-i
            t1=i-P[i]-1
            t2=i+P[i]+1
            while t1>=0 and t2<len(s) and s[t1]==s[t2]:
                P[i]+=1
                t1-=1
                t2+=1
        else:
            P[i]=P[2*C-i]
        if i+P[i]>R:
            R=i+P[i]
            C=i
    return P
P=malache(t)
index=P.index(max(P))
start=(index-P[index])//2
end=start+P[index]
return s[start:end]

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值