[ leeetcode 5] 最长回文子串 manacher算法详细解释

manacher算法 主要用来解决最长回文子串

研究了两天这个算法理论,终于搞清楚了,打算自己写详细一点,方便大家阅读,也方便自己以后复习看起来容易回忆。

参考了很多文献和他人写的代码,也看了一个视频,发现如果真想自己搞懂,必须亲自拿笔走一遍过程
这篇文章写的特别详细:最长回文字符串

参考
马拉车求最长回文子串
Manacher算法
最长回文串四种解法
python实现最长回文串

我自己的总结如下:

首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。

下面以字符串12212321为例,经过上一步,变成了 S[] = “$#1#2#2#1#2#3#2#1#”;

然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i],也就是把该回文串“对折”以后的长度),比如S和P的对应关系:

S  #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #
P  1  2  1  2  5  2  1  4  1  2  1  6  1  2  1  2  1
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)

注意:
有奇数回文串和偶数回文串,例如:aba,abba
加入特殊符号#以后,如何回文中心是#,则以前的回文串是偶数回文,如果回文中心是字母,则以前的回文串是奇数回文。

为什么用马拉车算法?因为加入特殊符号之后找中心点容易。

先附上代码,然后一步步讲解

def manacher(s):
    #预处理
    s='#'+'#'.join(s)+'#'
	#创建一个全是0的列表,用来存储以每个字符为中心点的回文长度的半径
    RL=[0]*len(s)
    #回文中心到右边最远的距离,即回文中心加上半径的距离-1
    MaxRight=0 
    #回文中心的index
    pos=0
    #原最长的回文串的长度
    MaxLen=0
    for i in range(len(s)):
        if i<MaxRight:
            RL[i]=min(RL[2*pos-i], MaxRight-i)
        else:
            RL[i]=1
        #尝试扩展,注意处理边界
        while i-RL[i]>=0 and i+RL[i]<len(s) and s[i-RL[i]]==s[i+RL[i]]:
            RL[i]+=1
        #更新MaxRight,pos
        if RL[i]+i-1>MaxRight:
            MaxRight=RL[i]+i-1
            pos=i
        #更新最长回文串的长度
        MaxLen=max(MaxLen, RL[i])
    print(RL)
    return MaxLen-1

if __name__ == '__main__':
    s = "babbad"
    # s1= 'abbbfdfbbssss'
    print(manacher(s))

s = b a b b a d
加入特殊符号之后:
s = # b # a # b # b # a # d #
index = 0 1 2 3 4 5 6 7 8 9 10 11 12
MaxRight=0 #回文中心的index
pos=0 #原最长的回文串的长度
MaxLen=0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值