给你一个字符串 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]