LeetCode【0005】最长回文子串
1 中文题目
给你一个字符串 s s s,找到 s s s 中最长的回文子串。回文串是一个正读和反读都相同的字符串.
示例 1:
- 输入: s = " b a b a d " s = "babad" s="babad"
- 输出: " b a b " "bab" "bab"
- 解释: " a b a " "aba" "aba"同样是符合题意的答案。
示例 2:
- 输入: s = " c b b d " s = "cbbd" s="cbbd"
- 输出: " b b " "bb" "bb"
提示:
- 1 ≤ s . l e n g t h ≤ 1000 1 \leq s.length\leq 1000 1≤s.length≤1000
- s s s 仅由数字和英文字母组成
2 求解思路
2.1 基础解法:暴力解法
思路
遍历所有可能的子串,对每个子串判断是否为回文串,并更新最长回文串的信息
- 步骤1: 外层循环确定子串起始位置
- 步骤2: 内层循环确定子串结束位置
- 步骤3: 判断当前子串是否为回文串
- 步骤4: 如果是回文串且长度更长,则更新记录
- 步骤5: 返回最长的回文子串
Python代码
class Solution:
def longestPalindrome(self, s: str) -> str:
"""
寻找字符串中的最长回文子串 - 暴力解法
Args:
s: 输入字符串
Returns:
返回最长的回文子串
"""
def isPalindrome(start: int, end: int) -> bool:
"""
判断子串是否为回文串的辅助函数
Args:
start: 子串的起始索引
end: 子串的结束索引
Returns:
如果是回文串返回True,否则返回False
"""
# 使用双指针从两端向中间移动
while start < end:
# 如果对应位置的字符不相等,则不是回文串
if s[start] != s[end]:
return False
# 向中间移动
start += 1
end -= 1
return True
# 获取字符串长度
n = len(s)
# 特殊情况处理:空串或单字符
if n < 2:
return s
# 记录最长回文子串的长度
max_len = 1
# 记录最长回文子串的起始位置
start = 0
# 外层循环:子串的起始位置
for i in range(n):
# 内层循环:子串的结束位置
for j in range(i + 1, n):
# 当前子串长度
sub_len = j - i + 1
# 如果当前子串长度大于已知最大长度,并且是回文串
if sub_len > max_len and isPalindrome(i, j):
# 更新最大长度和起始位置
max_len = sub_len
start = i
# 返回最长回文子串
return s[start:start + max_len]
时空复杂度分析
- 时间复杂度分析
- 外层循环,遍历所有起始位置: O ( n ) O(n) O(n)
- 内层循环,对每个起始位置遍历所有可能的结束位置: O ( n ) O(n) O(n)
- 判断回文: O ( n ) O(n) O(n)
总复杂度: O ( n ) ∗ O ( n ) ∗ O ( n ) = O ( n 3 ) O(n) * O(n) * O(n) = O(n³) O(n)∗O(n)∗O(n)=O(n3)
- 空间复杂度分析:O(1)
- 只使用了常数个变量
- 不需要额外的数组或矩阵
- 没有递归调用栈
2.2 优化解法:中心拓展法
思路
回文串是以中心点对称的,从每个可能的中心点向两边扩展,分别处理奇数长度和偶数长度的情况
- 步骤1: 遍历每个可能的中心点
- 步骤2: 对每个中心点,分别处理奇数和偶数长度的情况
- 步骤3: 从中心向两边扩展,直到不满足回文条件
- 步骤4: 记录并更新最长回文子串的位置
- 步骤5: 返回最长的回文子串
python代码
class Solution:
def longestPalindrome(self, s: str) -> str:
"""
使用中心扩展法查找最长回文子串
Args:
s: 输入字符串
Returns:
返回最长的回文子串
"""
def expandAroundCenter(left: int, right: int) -> tuple:
"""
从中心向两端扩展,寻找最长回文子串
Args:
left: 左边界起始位置
right: 右边界起始位置
Returns:
返回扩展后的左右边界位置
"""
# 当左右指针都在有效范围内,且对应字符相等时继续扩展
while left >= 0 and right < len(s) and s[left] == s[right]:
# 向左扩展
left -= 1
# 向右扩展
right += 1
# 返回最后一次有效的回文串边界
# left + 1: 因为while循环中left多减了1
# right - 1: 因为while循环中right多加了1
return left + 1, right - 1
# 特殊情况处理
if not s or len(s) < 2:
return s
# 记录最长回文子串的起始和结束位置
start = 0
end = 0
# 遍历每个可能的中心点
for i in range(len(s)):
# 处理奇数长度的回文串,以单个字符为中心
left1, right1 = expandAroundCenter(i, i)
# 处理偶数长度的回文串,以两个字符之间的空隙为中心
left2, right2 = expandAroundCenter(i, i + 1)
# 更新最长回文子串的位置
# 比较当前找到的两种回文串和之前找到的回文串的长度
if right1 - left1 > end - start:
start, end = left1, right1
if right2 - left2 > end - start:
start, end = left2, right2
# 返回最长回文子串
return s[start:end + 1]
时空复杂度分析
- 时间复杂度分析:
O
(
n
2
)
O(n²)
O(n2)
- 遍历中心点,奇数长度共有n个可能的中心点;偶数长度共有n-1个可能的中心点: O ( n ) O(n) O(n)
- 中心扩展,每次扩展最多需要n/2次比较: O ( n ) O(n) O(n)
总复杂度: O ( n ) ∗ O ( n ) = O ( n 2 ) O(n) * O(n) = O(n²) O(n)∗O(n)=O(n2)
- 空间复杂度分析:
O
(
1
)
O(1)
O(1)
- 只需要常数个变量记录位置信息
- 不需要额外的数组或矩阵
- 没有递归调用栈
2.3 最优解法:Manacher算法
思路
通过预处理将所有可能的中心点变成奇数形式,利用回文串的对称性质避免重复计算,记录并复用之前计算的结果。
- 步骤1: 字符串预处理
- 在字符间插入特殊字符’#’
- 统一奇偶长度的回文串处理方式
- 步骤2: 初始化状态
- 初始化p数组
- 设置center和right初始值
- 步骤3: 遍历处理每个位置
- 利用对称性获取初始回文半径
- 进行中心扩展
- 更新最大右边界和中心点
- 更新最长回文串信息
- 步骤4: 还原结果
- 将处理后的结果转换回原始字符串
python代码
class Solution:
def longestPalindrome(self, s: str) -> str:
"""
使用Manacher算法查找最长回文子串
Args:
s: 输入字符串
Returns:
返回最长的回文子串
"""
# 特殊情况处理
if not s or len(s) < 2:
return s
# Step 1: 预处理字符串,插入特殊字符 '#'
# 例如:"abc" -> "#a#b#c#"
t = '#' + '#'.join(s) + '#'
n = len(t)
# p[i]数组记录以i为中心的回文半径(不包括中心点)
p = [0] * n
# 维护已找到的回文子串的最右边界right及其对应的中心点center
center = 0 # 当前最大回文串的中心位置
right = 0 # 当前最大回文串的右边界
# 记录最长回文串的相关信息
max_len = 0 # 最长回文串的长度
max_center = 0 # 最长回文串的中心位置
# Step 2: 核心算法部分
for i in range(n):
if i < right:
# 利用回文串的对称性,获取初始回文半径
# mirror是i关于center的对称点
mirror = 2 * center - i
# 避免超出right边界,取较小值
p[i] = min(right - i, p[mirror])
# Step 3: 中心扩展
# 尝试扩展回文串的范围
left = i - (p[i] + 1) # 当前回文串的左边界
r = i + (p[i] + 1) # 当前回文串的右边界
# 在不越界的情况下继续扩展
while left >= 0 and r < n and t[left] == t[r]:
p[i] += 1
left -= 1
r += 1
# Step 4: 更新right和center
# 如果新的回文串的右边界超过了当前的right
if i + p[i] > right:
center = i
right = i + p[i]
# Step 5: 更新最长回文串的信息
if p[i] > max_len:
max_len = p[i]
max_center = i
# Step 6: 还原原始字符串中的回文子串
# 去除特殊字符'#',计算在原字符串中的起始位置和长度
start = (max_center - max_len) // 2
return s[start: start + max_len]
时空复杂度分析
- 时间复杂度分析
- 预处理字符串: O ( n ) O(n) O(n)
- 主循环处理: O ( n ) O(n) O(n)
- 中心扩展:均摊 O ( 1 ) O(1) O(1)
时间总体复杂度: O ( n ) O(n) O(n)
- 空间复杂度分析:
O
(
n
)
O(n)
O(n)
- p数组: O ( n ) O(n) O(n)
- 预处理后的字符串: O ( n ) O(n) O(n)
- 其他变量: O ( 1 ) O(1) O(1)
总空间复杂度: O ( n ) O(n) O(n)
3 题目总结
题目难度: 中等
数据结构: 字符串
应用算法:Manacher算法、双指针
原文地址:https://blog.csdn.net/qq_32882309/article/details/143673009
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!