题目:402-小M的最长回文子串挑战 题目解析
问题描述
小M发现了一个神奇的现象,她手中有一个字符串 s,她想知道这个字符串中最长的回文子串的长度是多少。你的任务是帮助小M找到该字符串的最长回文子串,并返回它的长度。
回文子串是指从左到右和从右到左字符顺序相同的字符串。
测试样例
样例1:
输入:
s = "abcbadb"
输出:5
样例2:
输入:
s = "ababacab"
输出:5
样例3:
输入:
s = "racecarwk"
输出:7
问题理解
- 给定一个字符串,需要找出其中最长的回文子串的长度
- 回文串指正着读和反着读都一样的字符串
- 需要考虑所有可能的子串,找出最长的回文子串长度
解题思路
动态规划基本思想
这道题采用动态规划的方法解决,主要思路如下:
- 使用二维数组
dp[i][j]表示字符串从位置i到j的子串是否为回文串 - 从小到大遍历子串长度,逐步判断各个长度的子串是否为回文串
动态规划状态设计
- 状态定义:
dp[i][j]表示s[i:j+1]是否为回文串(布尔值)
- 初始状态:
- 单个字符都是回文串:
dp[i][i] = True
- 单个字符都是回文串:
状态转移方程
对于长度大于1的子串,状态转移方程为:
dp[i][j] = (s[i] == s[j] and dp[i+1][j-1])
特殊情况:当子串长度为2时,只需判断两个字符是否相同:
dp[i][j] = (s[i] == s[j])
代码实现步骤
基础框架设置
def solution(s: str) -> int:
if not s:
return 0
n = len(s)
max_len = 1 # 初始化最大长度为1
dp = [[False] * n for _ in range(n)] # 创建dp数组
初始化单字符情况
# 单个字符都是回文串
for i in range(n):
dp[i][i] = True
遍历所有可能的子串
for length in range(2, n + 1): # 遍历所有可能的长度
for start in range(n - length + 1): # 遍历所有起始位置
end = start + length - 1
判断子串是否为回文
if length == 2:
dp[start][end] = (s[start] == s[end])
else:
dp[start][end] = (s[start] == s[end] and dp[start + 1][end - 1])
更新最大长度
if dp[start][end]:
max_len = max(max_len, length)
复杂度分析
- 时间复杂度:O(n²),其中n为字符串长度
- 空间复杂度:O(n²),需要n×n的dp数组
收获
- 如果字符串很长,可以考虑使用中心扩展法来降低空间复杂度
- 可以在发现更长的回文串时记录起始位置,方便返回具体的回文子串
- 可以添加提前退出的条件,如果已找到长度为n的回文串,就不需要继续搜索了
完整代码
def solution(s: str) -> int:
if not s:
return 0
# 初始化最大长度为1
max_len = 1
n = len(s)
# dp[i][j]表示s[i:j+1]是否为回文串
dp = [[False] * n for _ in range(n)]
# 单个字符都是回文串
for i in range(n):
dp[i][i] = True
# 遍历所有可能的长度
for length in range(2, n + 1):
# 遍历所有起始位置
for start in range(n - length + 1):
end = start + length - 1
# 如果长度为2,只需判断两个字符是否相同
if length == 2:
dp[start][end] = (s[start] == s[end])
else:
# 当前字符相同且子串为回文时,当前串为回文
dp[start][end] = (s[start] == s[end] and dp[start + 1][end - 1])
# 更新最大长度
if dp[start][end]:
max_len = max(max_len, length)
return max_len
if __name__ == '__main__':
print(solution("abcbadb") == 5)
print(solution("ababacab") == 5)
print(solution("racecarwk") == 7)