最长回文子串——中心扩散法
题目内容
给你一个字符串 s,找到 s 中最长的回文子串
# 示例 1:
# 输入
>>>s = "babad"
# 输出
>>>"bab"
# "aba" 同样是符合题意的答案。
# 示例 2:
# 输入
>>>s = "cbbd"
# 输出
>>>"bb"
# 示例 3:
# 输入
>>>s = "a"
# 输出
>>>"a"
# 示例 4:
# 输入
>>>s = "ac"
# 输出
>>>"a"
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
解题方法
不过多介绍回文子串的定义,这里随便举几个例子:
我们来分析一波示例:
1. 一个字符串的回文子串可能有多个解,本题没有限制必须是第一个解。(但我就想写成第一个解)
2. 即使是两个连起来的同样的字符串,同样可以认为是回文子串。
3. 长度为 1 的字符串也被认为是回文子串。
我们已经清楚本题回文子串的判定情况,下面我们构思本题的实现方法。
考虑到一般的回文子串长度都为奇数(见上图),但本题将两个连着的同样字符的字符串也判定为回文子串,因此会出现长度为偶数的回文子串。其对称中心不再是某个元素,而是介于最中间的两数之间。
为了简化程序,我们将原字符串字符之间插入空格(原字符串的字符只为数字与字母)作为分隔符,并在字符串首尾添加空格。
# 由于空格不易观察,这里用字符'&'代替
old -> new
abcbd -> &a&b&c&b&d&
# 实现方式
length = len(s)
newStr = []
for i in range(2 * length + 1):
if i % 2 == 0:
newStr.append(' ')
else:
newStr.append(s[(i - 1) // 2])
这里我们要考虑到,由于字符串长度发生了变化,所以找到回文子串后的左右索引不能直接返回,需要通过索引关系进行换算。
# 由于空格不易观察,这里用字符'&'代替
old -> new
0 1 2 3 4 -> 0 1 2 3 4 5 6 7 8 9 10
a b c b d -> & a & b & c & b & d &
# 假设原字符串索引为i, 新字符串索引为j
j = i * 2 + 1
这道题我采用的是中心扩散法:
1. 遍历新的列表,假设每个元素作为回文子串的中心点。
2. 接着判断其左右元素是否一致,若一致,则扩大回文子串,记录并继续判断下一层;反之评估下一个元素。
# 设置最终变量
maxLength = 1
maxLeftIndex, maxRightIndex = 0, 0
for i in range(2 * length + 1):
# 设置循环变量
pLength = 0
# 将左右指针归位
leftIndex = i
rightIndex = i
# 循环判断最外侧是否为相同字符,能否扩大回文子串
while True:
# 限制边界条件,避免指针越界
if leftIndex < 0 or rightIndex > (2 * length):
break
# 判断字符,更新变量
if newStr[leftIndex] == newStr[rightIndex]:
leftIndex -= 1
rightIndex += 1
pLength += 2
else:
break;
# 只有更大的回文子串出现时才更新全局变量,注意(pLength-1)才是回文子串长度
if (pLength - 1) > maxLength:
maxLength = pLength - 1
maxLeftIndex = leftIndex
maxRightIndex = rightIndex
要注意,每次判断回文子串结束后,左右指针一定指向上文我们插入的空格字符上。
这是因为最外侧字符不相同的情况只会发生在上一次判断的是空格字符上。
因此,我们需要将左右指针拉回到正确的边界上:
maxLeftIndex = maxLeftIndex + 2
maxRightIndex = maxRightIndex - 2
又因为我们得到的是新索引,所以我们需要根据上文的新旧索引换算公式进行换算:
realLeftIndex = (maxLeftIndex+1)//2
realRoghtIndex = (maxRightIndex-3)//2
最后返回计算出的最长回文子串,不要落下了最后一个字符!
returnStr = s[(maxLeftIndex+1)//2:(maxRightIndex-1)//2]
代码实现
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
maxLength = 1
maxLeftIndex, maxRightIndex = 0, 0
newStr = []
for i in range(2 * length + 1):
if i % 2 == 0:
newStr.append(' ')
else:
newStr.append(s[(i - 1) // 2])
for i in range(2 * length + 1):
pLength = 0
leftIndex = i
rightIndex = i
while True:
if leftIndex < 0 or rightIndex > (2 * length):
break
if newStr[leftIndex] == newStr[rightIndex]:
leftIndex -= 1
rightIndex += 1
pLength += 2
else:
break;
if (pLength - 1) > maxLength:
maxLength = pLength - 1
maxLeftIndex = leftIndex
maxRightIndex = rightIndex
returnStr = s[(maxLeftIndex+1)//2:(maxRightIndex-1)//2]
return returnStr
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n)
执行结果
欢迎各位大佬交流讨论
本文参考leecode题库第5题官方题解