【LeetCode】5. 最长回文子串

5. 最长回文子串

原题链接戳这⬅️

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

示例 1:

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

示例 1:

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

解题思路

解法一:动态规划

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示字符串 s [ i : j + 1 ] s[i:j + 1] s[i:j+1] 是否为回文字符串,其状态转移公式为:若 s [ i ] = = s [ j ] s[i] == s[j] s[i]==s[j] d p [ i ] [ j ] = d p [ i + i ] [ j − 1 ] dp[i][j] = dp[i + i][j - 1] dp[i][j]=dp[i+i][j1]; 否则, d p [ i ] [ j ] = F a l s e dp[i][j] = False dp[i][j]=False

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        if n < 2: return s
        
        dp = [[False] * n for _ in range(n)]
        # 单个字符均为回文字符串
        for i in range(n):
            dp[i][i] = True
        
        start,maxL = 0,1
        # 根据字符串长度遍历
        for L in range(2,n + 1):
            # 根据字符串左边界遍历:
            for i in range(n):
                j = i + L - 1
                if j >= n or s[i] != s[j]: continue
                # L= 2需要特殊考虑
                dp[i][j] = True if L == 2 else dp[i + 1][j - 1]
                if dp[i][j] and L > maxL: start,maxL = i,L

        return s[start:start+maxL]

时间复杂度 = O ( N 2 ) O(N^2) O(N2),空间复杂度 = O ( N 2 ) O(N^2) O(N2)

解法二:中心扩展

本方法不同于上一种遍历左右边界,而是枚举可能存在的回文字符串的中心,尝试向左右两边堆成扩展。
如果两侧字符相同,则继续扩展,反之,得到当前中心下单最长回文字符串。
Trick: 对于偶数长度的回文字符串,他的回文中心是虚的。因此,为了方便我们做枚举,我们首先对原始字符串进行填充,即:在每个字符串间隔中添加一个新字符。
也就是说,字符串 abc 会被填充为 #a#b#c#。那么得到的长度 // 2 即为原始的回文字符串长度。
注意添加的字符,即使在原始字符串中出现过,也不影响,因为他不可能和原始字符产生对应关系(上述例子中,#只会和#判断对应关系)

class Solution:
    def longestPalindrome(self, s: str) -> str:
        if len(s) < 2: return s
        expanded = "#"
        for c in s:
            expanded += c + '#'
        start,end,n = 0,0,len(expanded)
        for i in range(len(expanded)):
            for r in range(min(n - i,i + 1)):
                if expanded[i - r] != expanded[i + r]: 
                    r -= 1
                    break
            if end - start < r * 2: start,end = i - r, i + r

        return expanded[start + 1:end:2]

时间复杂度 = O ( N 2 ) O(N^2) O(N2),空间复杂度 = O ( N ) O(N) O(N)

解法三:Manacher算法

首先明确几个概念:
回文中心及右边界: 回文中心指回文串对对称中心,偶数长度的字符串回文中心是虚轴;右边界指回文字符串的结尾。
回文直径 / 半径: 直径集字符串长度,半径即从回文中心道右边界的长度。如:aba 的回文半径为 2 2 2,直径为 3 3 3aa 的回文半径为 1 1 1,直径为 2 2 2

  • 和暴力解相似,我们先对原始字符串s进行扩充,而后遍历字符串。
  • 当访问到位置 i i i,分为以下几种情况讨论:
    1. i i i >= R R R : 和暴力解一样向外扩,计算以字符最大长度
    2. i i i < R R R : 可以找到关于当前回文中心对称的 i 2 i2 i2 及当前回文子串的左边界 L L L
      1. 如果以 i 2 i2 i2为中心的回文子串在 L R L R LR 内部,则以 i i i为中心的回文子串直径与他相同
      2. 如果以 i 2 i2 i2为中心的回文子串在 L R L R LR 外部,则以 i i i为中心的回文子串半径是 R − i + 1 R - i + 1 Ri+1
      3. 如果以 i 2 i2 i2为中心的回文子串左边界为 L L L ,则以 i i i为中心的回文子串半径至少 R − i + 1 R - i + 1 Ri+1,基于此再尝试扩充
    class Solution:
    def expand(self,s,min_r,max_r,i):
        for r in range(min_r,max_r):
            if expanded[i - r] != expanded[i + r]: 
                r -= 1
                break
        return r

    def longestPalindrome(self, s: str) -> str:
        if len(s) < 2: return s
        expanded = "#"
        for c in s:
            expanded += c + '#'

        start,end,n = 0,0,len(expanded)
        alens,right,center = [],-1,-1
        for i in range(len(expanded)):
            if i >= right:
                r = self.expand(s,0,min(i + 1,n - i))
                if end - start < r * 2: start,end = i - r, i + r
                right,center = end,i
                alens.append(r)
            else:
                left = center - alens[center]
                i2 = 2 * center - i
                i2left = i2 - alens[i2]
                if i2left > left: alens.append(alens[i2])
                elif i2left < left: alens.append(right - i + 1)
                else:
                    r = self.expand(s,right - i + 1,min(n - i,i + 1),i)
                    if end - start < r * 2: start,end = i - r, i + r
                    right,center = end,i
                    alens.append(r)

        return expanded[start + 1:end:2]

时间复杂度 = O ( N ) O(N) O(N),空间复杂度 = O ( N ) O(N) O(N)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值