📜  最长回文子串 |设置 2(1)

📅  最后修改于: 2023-12-03 15:40:17.048000             🧑  作者: Mango

最长回文子串

什么是回文字符串

回文字符串就是正着和倒着读都一样的字符串。比如:"level"、"racecar"、"madam"都是回文字符串。而"hello"、"world"则不是。

问题描述

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

解决方案
暴力枚举法

最朴素的思路当然是从字符串的第一个字符开始,枚举所有子串,对于每个子串都判断一下是不是回文串,最后找到最长的那个回文串。这样的时间复杂度是O(n^3),由于n=1000,所以这个算法会非常慢。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        ans = ''
        for i in range(n):
            for j in range(i, n):
                if s[i:j+1] == s[i:j+1][::-1]:
                    if len(s[i:j+1]) > len(ans):
                        ans = s[i:j+1]
        return ans
中心拓展算法

可以思考一下回文串的性质:若s[l:r+1]是一个回文串,那么s[l+1:r]也必定是回文串。基于这个性质,我们可以枚举中心位置i,向两边拓展,直到找到最长的回文串。

但是需要特别处理两种情况:回文串长度是奇数的情况,和回文串长度是偶数的情况。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        start, end = 0, 0
        for i in range(n):
            # 回文串长度为奇数
            left, right = i, i
            while left >= 0 and right < n and s[left] == s[right]:
                left -= 1
                right += 1
            if right - left - 1 > end - start:
                start = left + 1
                end = right
            # 回文串长度为偶数
            left, right = i, i + 1
            while left >= 0 and right < n and s[left] == s[right]:
                left -= 1
                right += 1
            if right - left - 1 > end - start:
                start = left + 1
                end = right
        return s[start:end]
动态规划

如果一个字符串是回文串,那么去掉两端的字符后剩下的字符串也是回文串。基于这个性质,我们可以设计一个动态规划算法。

定义状态f(i, j)表示s[i:j+1]是否是回文串,如果是,f(i, j)为True,否则为False。

状态转移方程:

  • f(i, j) = True,如果s[i] == s[j]且f(i+1, j-1) = True
  • f(i, j) = False,否则

边界条件:

  • f(i, i) = True
  • f(i, i+1) = True,如果s[i] == s[i+1]
class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        f = [[False] * n for _ in range(n)]
        start, end = 0, 0
        for i in range(n):
            for j in range(i+1):
                if s[i] == s[j] and (i - j < 2 or f[j+1][i-1]):
                    f[j][i] = True
                    if i - j + 1 > end - start:
                        start = j
                        end = i
        return s[start:end+1]

这个算法的时间复杂度是O(n^2),比中心拓展算法优秀一些,但空间复杂度是O(n^2),需要开辟一个二维数组。但是可以通过滚动数组将空间复杂度降到O(n)。