LeetCode 热题 HOT 100 第五天 5. 最长回文子串 中等题 用python3求解

题目地址

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

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

示例 2:
输入:s = “cbbd”
输出:“bb”

提示:
1 <= s.length <= 1000
s 仅由数字和英文字母组成

在这里插入图片描述

回文子串:正着读、反着读都是一样的字符串

解法:改进中心探测法
在这里插入图片描述
在这里插入图片描述

注意:这里的奇数偶数是指最终结果的最长回文子串长度,而不是原字符串的长度

以babba这个字符串为例,是奇数长度的字符串,但最终的最长回文子串是abba,是偶数长度的,所以在代码里面是要用偶数情况的两个不同位置的指针 res2 = num(i, i + 1)才能得到结果abba。(用奇数情况的res1 = num(i, i)得到的是bab)

以bada这个偶数长度的字符串为例,最终的最长回文子串是ada,是奇数长度的。用奇数情况的res1 = num(i, i)得到的是ada是最终结果。用偶数情况的 res2 = num(i, i + 1)只能得到单个字符。

代码实现(递归):

class Solution:
    def longestPalindrome(self, s: str) -> str:
        size = len(s) #首先得到字符串的长度,方便逐个点遍历
        res = [] #因为要返回一个最长子串,所以初始化一个返回参数
        max_val = 0
        def num(loc_left, loc_right): #定义一个以某个点为中心,寻找最长子串的函数
            while loc_left >= 0 and loc_right < size:#因为要找到最长子串,所以要利用while函数不停的往两个方向寻找
                if s[loc_left] == s[loc_right]: #如果左边点与右边点的元素相同,则继续往两边遍历
                    loc_left -= 1 #往两边遍历,自然就是左边角标减1
                    loc_right += 1 #往两边遍历,自然就是右边角标加1
                else: #如果发现不一样的,直接跳出,说明不是回文串
                    break
            return s[loc_left + 1: loc_right] #函数的返回值即为我们找到的以当前点为中心的最常子串
        for i in range(size): #因为要以每个点为中心寻找,自然要逐个遍历
            res1 = num(i, i) #此时为奇数情况,以一个点为中心
            res2 = num(i, i + 1) #此时为偶数情况,以相邻两个点为中心
            if max(len(res1), len(res2)) > max_val: #比较两种情况为中心返回的最长子串的长度
                max_val = max(len(res1), len(res2))#如果当前返回长度比之前的大,则更新max_val,即最长子串的长度
                if len(res1) > len(res2): #此时判断是哪种情况的子串长,更新返回函数
                    res = res1
                else:
                    res = res2
        return res #返回的即为最长子串

另一种解法:动态规划

动态规划相关基础入门+案例:地址

动态规划:填dp表、当前ij状态、过去ij状态、如何联合得到输出、边界条件

1、定义状态:题目让我们求什么,就把什么设置为状态
题目求s中最长的回文子串,那就判断所有子串是否为回文子串,选出最长的
因此:dp[i][j]表示s[i:j+1]是否为回文子串(这里+1是为了构造闭区间)因为python里面数组是左闭右开,[i:j+1]是包括i但不包括j+1,代表i到j

2、状态转移方程:对空间进行分类讨论(当前ij状态、过去ij状态 如何联合得到输出)

当前ij状态:头尾必须相等(s[i]==s[j])

过去ij状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)

边界条件:只要是找过去ij状态的时候,就会涉及边界条件(即超出边界情况处理)

当i==j时一定是回文

j-1-(i+1)<=0,即j-i<=2时,只要当s[i]==s[j]时就是回文,不用判断dp[i+1][j-1]

dp[i][j] 为截取的子串

3、初始状态:这里已经直接判断j-i<=2的情况了,因此用不到初始状态,可以不设

4、输出内容:每次发现新回文都比较一下长度,记录i与长度

5、优化空间提速

在这里插入图片描述

代码实现:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        
        size = len(s)
        # 特殊处理
        if size == 1:
            return s
        # 创建动态规划dynamic programing表
        dp = [[False for _ in range(size)] for _ in range(size)]
        # 初始长度为1,这样万一不存在回文,就返回第一个值(初始条件设置的时候一定要考虑输出)
        max_len = 1
        start = 0
        for j in range(1,size):
            for i in range(j):
                # 边界条件:
                # 只要头尾相等(s[i]==s[j])就能返回True
                if j-i<=2:
                    if s[i]==s[j]:
                        dp[i][j] = True
                        cur_len = j-i+1
                # 状态转移方程 
                # 当前dp[i][j]状态:头尾相等(s[i]==s[j])
                # 过去dp[i][j]状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)
                else:
                    if s[i]==s[j] and dp[i+1][j-1]:
                        dp[i][j] = True
                        cur_len = j-i+1
                # 出现回文更新输出
                if dp[i][j]:
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i

        return s[start:start+max_len]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值