目录
题目
给你一个字符串 s
,找到 s
中最长的 回文子串
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd" 输出:"bb"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
思路
使用动态规划方法来解决最长回文子串问题。动态规划通过构建一个表格来存储中间结果,避免重复计算,从而提高效率。
解题过程
-
初始化:
- 创建一个
n×n
的布尔表dp
,dp[i][j]
表示子串s[i:j+1]
是否为回文。 - 初始化这个二维表。对于长度为1的子串(即单个字符),它们自然是回文串,因此
dp[i][i]
(所有对角线上的元素)都应该被设置为True
。
n = len(s)
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = True
-
填表:
- 回文子串的长度可以从2开始逐渐增加,可以从长度为2的子串开始检查,然后是长度为3的子串,依此类推,直到长度为
n
的子串。对于每个长度,我们遍历所有可能的起始索引。 - 对于任意
dp[i][j]
(其中i < j
),如果s[i] == s[j]
(即子串的两个端点字符相同),我们需要检查子串内部(即s[i+1:j]
)是否也是回文串。这可以通过查看dp[i+1][j-1]
的值来实现。如果dp[i+1][j-1]
为True
,则dp[i][j]
也为True
。此外,对于长度为2或3的子串,我们可以直接通过比较字符来判断,而无需检查内部子串,因为这样的子串要么只包含两个相同的字符(长度为2),要么包含三个字符且首尾相同、中间字符任意(长度为3,但首尾相同的情况已经通过比较字符确定了)。
for length in range(2, n+1): # 子串长度从2到n
for i in range(n - length + 1): # 遍历所有可能的起始索引
j = i + length - 1 # 计算结束索引
if s[i] == s[j] and (length == 2 or dp[i+1][j-1]):
dp[i][j] = True
- 更新最长回文子串的起始位置和长度:
在填表的过程中,我们还需要跟踪记录到目前为止找到的最长回文子串的起始位置和长度。这可以通过比较当前回文子串的长度和已记录的最长长度来实现,并在需要时更新这些信息
max_length = 1
start = 0
for length in range(2, n+1):
for i in range(n - length + 1):
j = i + length - 1
if dp[i][j] and length > max_length:
max_length = length
start = i
-
结果提取:
根据记录的最长回文子串的起始位置和长度,从原字符串中提取出这个子串作为结果。
longest_palindrome = s[start:start+max_length]
复杂度
- 时间复杂度: O(n^2),要检查字符串中所有可能的子串(即从每个索引开始,长度为2到n的所有子串),这导致了一个双重循环,每层循环都遍历了字符串的一部分。
- 空间复杂度: O(n^2),因为需要一个
n×n
的布尔表来存储每个子串是否为回文的状态。
code
class Solution(object):
def longestPalindrome(self, s):
if len(s) < 2:
return s
n = len(s)
max_len = 1
start = 0
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = True
for j in range(1, n):
for i in range(0, j):
if s[i] == s[j]:
if j - i < 3:
dp[i][j] = True
else:
dp[i][j] = dp[i + 1][j - 1]
if dp[i][j] and j - i + 1 > max_len:
max_len = j - i + 1
start = i
return s[start:start + max_len]
标签:子串,length,range,长度,最长,dp,回文
From: https://blog.csdn.net/Sxiaocai/article/details/141182236