victory的博客

长安一片月,万户捣衣声

0%

leetcode | 5.最长回文串

5.最长回文串

题目描述

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:
输入:s = “babad”
输出:”bab”
解释:”aba” 同样是符合题意的答案。

题目链接

思路

1.暴力解法
2.动态规划
3.中心扩展算法
4.Manacher 算法
注:算法详情请参考leetcode题解

几种方法的总结!!!强烈推荐!!!

代码

class Solution:
    def longestPalindrome(self, s: str) -> str:
        """暴力解法(超出时间限制)"""
        # max_len = 0
        # longest_palindrome = ""
        # for i in range(0, len(s)):
        #     for j in range(i+1, len(s)+1):
        #         string = s[i:j]
        #         # print(string)
        #         if self.isPalindrome(string):
        #             if len(string) > max_len:
        #                 max_len = len(string)
        #                 longest_palindrome = string
        #
        # return longest_palindrome

        len_s = len(s)
        if len_s < 2:
            return s

        max_len = 1
        begin = 0
        # 枚举所有长度严格大于1的字串
        for i in range(0, len_s-1):
            for j in range(i + 1, len_s):
                if j - i + 1 > max_len and self.isPalindrome(s[i: j+1]):
                    max_len = j - i + 1
                    begin = i

        return s[begin:begin + max_len]

    def isPalindrome(self, s: str) -> bool:
        """
        筛选+判断(判断反转字符串是否与原字符串相同)
        """
        new_s = "".join(ch.lower() for ch in s if ch.isalnum())
        return new_s == new_s[::-1]

    def longestPalindrome1(self, s: str) -> str:
        """
        动态规划算法

        一个回文串去掉两头以后,剩下的部分依然是回文

        -状态:dp[i][j]表示字串s[i..j]是否为回文子串
        -得到状态转移方程:dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]
         边界条件:j - 1 - (i + 1) + 1 < 2,整理得j - i < 3 <===> j - i + 1 < 4
         (s[i][j]长度为2或者3时,不用检查字串是否回文)
        -初始化:dp[i][i] = true
        -输出:在得到一个状态的值为true的时候,记录起始位置和长度,填表完成以后再截取

        状态转移方程:dp[i][j] = (s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1]
        """
        n = len(s)
        if n < 2:
            return s

        max_len = 1
        begin = 0

        # dp[i][j]表示s[i..j]是否是回文串
        dp = [[False] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = True

        # 注意:先填左下角
        for j in range(1, n):
            for i in range(0, j):
                if s[i] != s[j]:
                    dp[i][j] = False
                else:
                    if j - i < 3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i + 1][j-1]

                # 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
                if dp[i][j] and j - i + 1 > max_len:
                    max_len = j - i + 1
                    begin = i
        return s[begin:begin + max_len]

    def longestPalindrome2(self, s: str) -> str:
        """中心扩展算法"""
        start = 0  # 最长回文串的起始位置
        end = 0  # 最长回文串的结束位置
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i)  # 边界情况1:子串长度为1的情况
            left2, right2 = self.expandAroundCenter(s, i, i + 1)  # # 边界情况2:子串长度为2的情况
            if right1 - left1 > end - start:  # 扩展的新回文串长度大于当前最长回文串
                start, end = left1, right1
            if right2 - left2 > end - start:
                start, end = left2, right2
        return s[start:end + 1]

    def expandAroundCenter(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

    def expand(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return (right - left - 2) // 2

    def longestPalindrome3(self, s: str) -> str:
        """Manacher 算法"""
        end = -1
        start = 0
        s = '#' + '#'.join(list(s)) + '#'
        arm_len = []
        right = -1
        j = -1
        for i in range(len(s)):
            if right >= i:
                i_sym = 2 * j - i
                min_arm_len = min(arm_len[i_sym], right - i)
                cur_arm_len = self.expand(s, i - min_arm_len, i + min_arm_len)
            else:
                cur_arm_len = self.expand(s, i, i)
            arm_len.append(cur_arm_len)
            if i + cur_arm_len > right:
                j = i
                right = i + cur_arm_len
            if 2 * cur_arm_len + 1 > end - start:
                start = i - cur_arm_len
                end = i + cur_arm_len
        return s[start + 1:end + 1:2]


if __name__ == '__main__':
    slt = Solution()
    s = "abad"
    res = slt.longestPalindrome(s)
    print("最长的回文串为:", res)