5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
暴力解法(6004ms AC)
由最长的子串(本身)找起,当发现满足回文条件时返回结果。
维护当前子串长度 length
起始点由 0 到 距离尾部 length 的位置
根据起始点计算终点的索引,耗时很长,代码如下:
class Solution:
def longestPalindrome(self, s):
length = len(s)
if length <= 1:
return s
while length > 0:
for head in range(0, len(s)-length+1):
tail = head+length
if s[head:tail] == s[head:tail][::-1]:
return s[head:tail]
length -= 1
巧妙解法(132ms AC)
其关键思想在于:
在发现一个回文子串
S
j
.
.
.
S
k
S_j...S_k
Sj...Sk的基础上,进入下一个字符
S
k
+
1
S_{k+1}
Sk+1时,区分三种情况:
- 当前字符和前一个回文子串构成一个新的回文子串
S
j
.
.
.
S
k
+
1
S_j...S_{k+1}
Sj...Sk+1
- 当前字符和前一个回文子串以及子串的前一个字符构成新的回文子串
S
j
−
1
.
.
.
S
k
+
1
S_{j-1}...S_{k+1}
Sj−1...Sk+1
- 不构成新的回文子串,此时要查找的更长的回文子串长度应当为当前最长长度max_len+1 或 max_len+2
由于回文串在删去其头尾两端的字符时仍然是回文串,所以不必担心会遗漏结果。
在下面的代码中, i 始终控制查找的子串的结尾,目标是长度为 max_len+1 或者 max_len +2 的回文子串,当找到时,会进入情况一或情况二中,因此确保结果正确。
class Solution:
def longestPalindrome(self, s):
l=len(s)
maxl=0
start=0
for i in range(l):
# 情况二
if i-maxl>0 and s[i-maxl-1:i+1]==s[i-maxl-1:i+1][::-1]:
start=i-maxl-1
maxl+=2
# 情况一
elif s[i-maxl:i+1]==s[i-maxl:i+1][::-1]:
start=i-maxl
maxl+=1
# 情况三:什么也不做
return s[start:start+maxl]
2019.03.16增补方法
类暴力法(2184ms AC)
与6000ms的方法不同,我们遍历两次中心点,第一次针对奇数长度的情况,第二次针对偶数长度的情况,当发现非回文序列子串时,则记下当前最长子串,并进入下一个中心点。
class Solution:
def longestPalindrome(self, s):
if len(s) <= 1:
return s
mid = 0
tmp = s[0]
for mid in range(0,len(s)):
i = 1
while mid - i >=0 and mid +i < len(s):
if s[mid-i] == s[mid+i]:
if 2*i+1 > len(tmp):
tmp = s[mid-i:mid+i+1]
i += 1
else:
break
for mid in range(0, len(s)):
i = 1
while mid - i >=0 and mid +i-1 < len(s):
if s[mid-i] == s[mid+i-1]:
if 2*i > len(tmp):
tmp = s[mid-i:mid+i]
i += 1
else:
break
return (tmp)
类暴力法改进(124ms AC)
此方法在类暴力法的基础上只改进了一点,每次考虑新的子串时,从长度至少为max_length的中心开始比较。
class Solution:
def longestPalindrome(self, s):
if len(s) <= 1:
return s
mid = 0
tmp = s[0]
for mid in range(0,len(s)):
i = len(tmp)//2 # 仅在这里进行调整
while mid - i >=0 and mid + i < len(s):
if s[mid-i:mid+i+1] != s[mid-i:mid+i+1][::-1]:
break
else:
if 2*i+1 > len(tmp):
tmp = s[mid-i:mid+i+1]
i += 1
for mid in range(0, len(s)):
i = len(tmp)//2
while mid - i >=0 and mid +i-1 < len(s):
if s[mid-i:mid+i] != s[mid-i:mid+i][::-1]:
break
else:
if 2*i > len(tmp):
tmp = s[mid-i:mid+i]
i += 1
return (tmp)