leetcode-5 最长回文字符串 Manacher 算法 (python 代码)

题目:

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:

输入: “cbbd”
输出: “bb”

题解:
Manacher 算法,被中国程序员戏称为“马拉车”算法。专门用于解决“最长回文子串”问题,时间复杂度为 O(n)O(n),事实上,“马拉车”算法在思想上和“KMP”字符串匹配算法有相似之处,都避免做了很多重复的工作。“KMP”算法也有一个很有意思的戏称,带有一点颜色。

挺有意思的一件事情是:我在学习“树状数组”和“Manacher 算法”的时候,都看了很多资料,但最后代码实现的时候,就只有短短十几行。

理解 Manacher 算法最好的办法,是根据查阅的关于 Manacher 算法的文章,自己在稿纸上写写画画,举一些具体的例子,这样 Manacher 算法就不难搞懂了。

Manacher 算法本质上还是中心扩散法,只不过它使用了类似 KMP 算法的技巧,充分挖掘了已经进行回文判定的子串的特点,提高算法的效率。

下面介绍 Manacher 算法的运行流程。

首先还是“中心扩散法”的思想:回文串可分为奇数回文串和偶数回文串,它们的区别是:奇数回文串关于它的“中点”满足“中心对称”,偶数回文串关于它“中间的两个点”满足“中心对称”。为了避免对于回文串字符个数为奇数还是偶数的套路。首先对原始字符串进行预处理,方法也很简单:添加分隔符。

第 1 步:对原始字符串进行预处理(添加分隔符)
我们先给出具体的例子,看看如何添加分隔符。

例1:给字符串 "level" 添加分隔符 “#”。

答:字符串 "level" 添加分隔符 "#" 以后得到:"#le#v#e#l#"

例2:给字符串 "noon" 添加分隔符 "#"

答:字符串 "noon" 添加分隔符 "#" 以后得到:"#n#o#o#n#"

我想你已经看出来分隔符是如何添加的,下面是两点说明。

1、分隔符是一定是原始字符串中没有出现过的字符,这个分隔符的种类也只能有一个,即你不能同时添加 "#""?" 作为分隔符;

2、添加分隔符的方法是在字符串的首位置、尾位置和每个字符的“中间”都添加 11 个这个分隔符。可以很容易知道,如果这个字符串的长度是 len,那么添加的分隔符的个数就是 len + 1,得到的新的字符串的长度就是 2 * len + 1,显然它一定是奇数。

为什么要添加分隔符?

1、首先是正确性:添加了分隔符以后的字符串的回文性质与原始字符串是一样的(这句话不是很严谨,大家意会即可);

2、其次是避免回文串长度奇偶性的讨论(马上我们就会看到这一点是如何体现的)。

第 2 步:得到 p 值
p值为会文的最大半径:以 char[i] 作为回文中心,同时向左边、向右边进行“中心扩散”,直到“不能构成回文串”或“触碰到字符串边界”为止,能扩散的步数 + 1(这个 1 表示“中心”) ,即定义为 p 数组的值,也称之为“回文半径。
不断的循环迭代中心点,如果新的半径大于原来半径,那么就令p等于新的半径,并记录这个中心点的索引。
有了索引,有了最大半径,那么就可以得到最大回文字符串.

code:

class Solution:
    def longestPalindrome(self, s: str):
        s = '#' + '#'.join(s) + '#'
        s_len = len(s)-1
        index = 0
        p=0
        for i in range(1, len(s)):
            j=1
            while True:
                if  i<j or i+j>s_len or s[i-j]!=s[i+j] :
                    if j-1>p:
                        p = j-1
                        index = i
                    break
                j+=1
	return ''.join(s[index-p:index+p+1].split('#'))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值