LeetcodeMedium-【5. 最长回文子串】三种解法:中心扩散、动态规划、Manacher's Algorithm(马拉车算法)

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

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。


示例 2:

输入: "cbbd"
输出: "bb"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路1:【中心扩散法】记得以前做过很多回文的变形题,现在貌似又的差不多了。言归正传,这题可以向在每个字符间插入一个自定义符号例如#等,这样可以保证一定最大的回文字符串一定是奇数的,也就是一定以某个字符为中心,可以将(aba和aa)这两种一起处理了。然后现在我能想到的只有暴力了,遍历每一个字符,以它为中心,往前往后分别遍历,找到最长回文。

复杂度应该是:o(n^2)

class Solution:
    def longestPalindrome(self, s: str) -> str:
        s_2 = '#'
        for i in range(len(s)):
            s_2 += s[i] + '#'
        max_s = 0
        max_len = 0
        for i in range(1, len(s_2)):
            k = 1
            while i - k >= 0 and i + k < len(s_2):
                if s_2[i-k] == s_2[i+k]:
                    k += 1
                else:
                    break
            k -= 1
            if max_len < k * 2 + 1:
                max_s = i - k # 回文的起点
                max_len = k * 2 + 1
        #         print(max_s, k, max_len)
        # print(s_2)
        # print(max_s, max_len)
        # print(s_2[max_s:(max_s+max_len)].replace('#', ''))
        return s_2[max_s:(max_s+max_len)].replace('#', '')

思路2:动态规划

 对于字符串str,假设dp[i,j]=1表示str[i...j]是回文子串,那个必定存在dp[i+1,j-1]=1。这样最长回文子串就能分解成一系列子问题,可以利用动态规划求解了。

首先构造状态转移方程

上面的状态转移方程表示,当str[i]=str[j]时,如果str[i+1...j-1]是回文串,则str[i...j]也是回文串;如果str[i+1...j-1]不是回文串,则str[i...j]不是回文串。

初始状态

  • dp[i][i]=1

  • dp[i][i+1]=1 if str[i]==str[i+1]

上式的意义是单个字符,两个相同字符都是回文串。

注意递推时,按照长度一次为2,3,4...的顺序进行推算。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        length = len(s)
        dp = [[0]*length for _ in range(length)]
        for i in range(length):
            dp[i][i] = 1
        max_i = 0
        max_j = 0
        max_len = 0
        # 控制每次的区间长度-1
        for k in range(1, length): 
            # i为区间起始坐标,j为区间结束坐标
            for i in range(0, length - k):
                j = i + k
                if s[i] == s[j]:
                    # print(i, j)
                    if i == j - 1: # 长度为2,K=1时需要特判
                        dp[i][j] = 1
                    else:
                        dp[i][j] = dp[i+1][j-1]
                    if dp[i][j] == 1:
                        if k + 1 > max_len:
                            max_i = i
                            max_j = j
                            max_len = k + 1
                else:
                    dp[i][j] = 0
        return s[max_i:max_j+1]
                

思路3:Manacher's Algorithm(马拉车算法)

参考文档:https://blog.csdn.net/weixin_43272781/article/details/89713050

参考视频:UESTCACM 每周算法讲堂 manacher算法

                  湘潭大学2019年ACM集训专题-manacher&回文树

                【算法讲堂】【电子科技大学】【ACM】字符串算法选讲

本人总结下关键点:

①定义的几个变量:

p[]:  p[i]表示以i为中心的回文字符串的半径;

id : 表示目前延伸最远的回文字符串的半径;(并不是最大,只是最右的一个回文串)

mx: 表示以id为中心的回文串的右边界外第一个点位置;

②递推关系:

设i,j是与id为中心的对称点,那么在已知p[j]的情况下,需要计算p[i]就有以下两种情况:

1、p[j]在id的回文串内部,那么p[i]就也在内部,可以计算得到 p[i] = p[j] = p[2*id-i],因为i + j = 2 * id(id为中心)。

2、p[j]超过了id的回文串范围,那么p[i]最小也等于 mx - i。

总结为: p[i] = min(p[2*id-i], mx-i)

注意:此时得到目前的p[i]后依然要进行扩展以i为中心的回文串,因为此时获得的是最小半径。

复杂度:O(n)

class Solution:
    def longestPalindrome(self, s: str) -> str:
        s_2 = '#'
        for i in range(len(s)):
            s_2 += s[i] + '#'
        # print(s_2)
        length = len(s_2)
        # p[i]以i中心的回文半径
        p = [1] * length
        id = 0  # 某个延伸最远的回文字符串的中心(该回文串不一定最长) 
        mx = 1  # 某个延伸最远的回文字符串能达到的最右端的值+1(即在回文外的第一个值) 
        max_s = 0
        max_r = 0
        for i in range(length):
            if i < mx:
                p[i] = min(p[2*id-i], mx-i)
            while i - p[i] >= 0 and i + p[i] < length and s_2[i - p[i]] == s_2[i + p[i]]: # 拓展i为中心的回文串
                p[i] += 1
            if i + p[i] > mx: # 如果以i为中心的回文串延伸的更远,则进行更新
                mx = i + p[i]
                id = i
            if p[i] > max_r: # 跟新最大回文字符串
                max_s = i 
                max_r = p[i]
        # print(max_s, max_r)
        return s_2[max_s-max_r+1:max_s+max_r-1].replace('#', '')


        
                
发布了314 篇原创文章 · 获赞 22 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_39451578/article/details/105297586