leetcode(6)_5_medium_最长回文子串_python

最长回文子串

题目描述:
给你一个字符串 s,找到 s 中最长的回文子串。
示例 :
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母(大写和/或小写)组成

解法一:中心扩展

  1. 回文串有两种样式,第一种是长度为奇数的情况,会有一个中心点,例如aba;第二种是长度为偶数的情况,会有两个中心点,例如abba
  2. 基于此,一个直观的思路就是遍历,以每个字符为中心点,或者紧挨着的两个字符为中心点,然后向两边不断扩展,直到不满足回文的要求。
代码
class Solution:
    def longestPalindrome(self, s: str) -> str:
        res, max_len = s[0], 1
        for idx in range(len(s) - 1):
            substr1, len1 = Solution.func(s, idx, idx)  # 以当前字符为中心点
            substr2, len2 = Solution.func(s, idx, idx + 1)  # 以当前字符和下一个字符为中心点
            # 更新最优解
            [res, max_len] = [substr1, len1] if len1 > max_len else [res, max_len]
            [res, max_len] = [substr2, len2] if len2 > max_len else [res, max_len]
        return res
    
    def func(s: str, left: int, right: int) -> str:
        while left >= 0 and right < len(s) and s[left] == s[right]:  # 不越界并且两端字符相同
            right += 1
            left -= 1
        return s[left + 1: right], right - left - 1  # 返回子串和长度
      
测试结果

执行用时:1080 ms, 在所有 Python3 提交中击败了63.98% 的用户
内存消耗:14.8 MB, 在所有 Python3 提交中击败了95.04% 的用户

其他方式

思路其实是直观的,有一个可探讨的点是,怎么更新当前最优解。上述代码的思路就是把子串和长度都返回,然后如果有更长的子串,就更新最优子串。其实也有很多其它的方式,举一个更为简单的例子,就是整个过程只记录最优子串的两个端点,最后再截取即可。代码:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        left, right = 0, 0
        for idx in range(len(s) - 1):
            left1, right1 = Solution.func(s, idx, idx)
            left2, right2 = Solution.func(s, idx, idx + 1)
            if right1 - left1 > right - left:
                left, right = left1, right1 
            if right2 - left2 > right - left:
                left, right = left2, right2 
        return s[left: right + 1]
    
    def func(s: str, left: int, right: int) -> str:
        while left >= 0 and right < len(s) and s[left] == s[right]:
            right += 1
            left -= 1
        return left + 1, right - 1
        
# 执行用时:924 ms, 在所有 Python3 提交中击败了75.49% 的用户
# 内存消耗:15.1 MB, 在所有 Python3 提交中击败了50.91% 的用户

解法二:动态规划

  1. 从解法一也可以看出,回文串可以看作不断向两边扩展的过程,这样的话就可以使用动态规划的方法。但其实我个人觉得解法一的思路更为直观一点。
  2. 我们使用二维布尔数组 dp[][] 来记录回文串的情况:
dp[i][j] = True if s[i: j+1] is a palindromic-substring else False
  1. 我们也可以推出更新公式:
			True, if i == j
dp[i][j] =	s[i] == s[j], if i == j-1
			dp[i+1][j-1] and s[i] == s[j], otherwise
  1. 其实比较关键的是怎么进行更新,因为大的回文串是建立在小的回文串的基础上,所以我们应该先更新所有长度为1的回文串,进而更新长度为 2,3,··· 的回文串。
代码
class Solution:
    def longestPalindrome(self, s: str) -> str:
        n, start, end = len(s), 0, 0
        dp = [[False] * n for _ in range(n)]
        for k in range(n):  # k 是间隔,间隔为 k,子串长度为 k+1
            for i in range(n - k):
                j = i + k
                if k == 0:
                    dp[i][j] = True
                elif k == 1:
                    dp[i][j] = s[i] == s[j]
                else:
                    dp[i][j] = dp[i+1][j-1] and s[i] == s[j]
                if dp[i][j]:  # 因为 k 越来越大,字串的长度也是不断增大的。
                    start, end = i, j
        return s[start: end + 1]
        
测试结果

执行用时:9172 ms, 在所有 Python3 提交中击败了13.87% 的用户
内存消耗:22.5 MB, 在所有 Python3 提交中击败了33.19% 的用户

说明

算法题来源:力扣(LeetCode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值