5. Longest Palindromic Substring
Medium
6919537Add to ListShare
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
方法1:O(n^3) 暴力
从最长的子串(它本身)开始判断是否是回文,如果是则return,然后在判断长度减1的子串是否是回文。比如“babad”,先判断子串长度为5的所有子串是否是回文,如果是return,否则继续判断子串长度为4的所有子串是否是回文,如果某个子串是回文,则return
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
if length < 2:
return s
while length > 0:
for i in range(len(s)):
l, r = i, i + length
if r <= len(s):
if self.isPalindrome(s[l:r]):
return s[l:r]
else:
break
length -= 1
def isPalindrome(self, s: str):
i, j = 0, len(s) - 1
while i <= j:
if s[i] == s[j]:
i += 1
j -= 1
else:
break
if i > j:
return True
return False
方法2:O(n^2) 从中心向两边扩散
(1)将子串分为奇数串和偶数串
(2)从index=0开始一直到index=len(s) - 1, 奇数的时候left=index, right=index,如果s[left]==s[right],left减一,right加1知道不相等或者超出边界范围,同时比较当前满足回文的子串的长度与当前最长子串的长度,如果更长,则更新最长子串的长度。偶数的时候,left=index, right=index+1,剩下操作一样。我们以初始的s[index]看做子串的中间点,向两边扩散,当向两边扩散的时候偶数与奇数情况都要同时考虑
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
if length < 2:
return s
l , r = 0, 0
def palindrome(s: str, left: int, right: int):
nonlocal l, r
while left >= 0 and right < length and s[left] == s[right]:
left -= 1
right += 1
if right - left - 1 > r - l + 1:
l, r = left + 1, right - 1
for i in range(length - 1):
palindrome(s, i, i)
palindrome(s, i, i + 1)
return s[l:r + 1]
方法3:动态规划 O(n^2)
为了在验证回文的过程中减少不必要的比较,比如我们已经知道“bab”是一个回文,那么我们在判断"ababa"的时候,只需要比较左右两边的两个字母是否相同就可以了
注释掉的部分可以用一句代码替代
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
if length < 2:
return s
dp = [[False] * length for i in range(length)]
rlt = ""
for j in range(length):
for i in range(j + 1):
dp[i][j] = s[i] == s[j] and (j - i <= 2 or dp[i + 1][j - 1])
# if i == j:
# dp[i][j] = True
# elif j - i == 1:
# dp[i][j] = s[i] == s[j]
# elif j - i > 1:
# dp[i][j] = s[i] == s[j] and dp[i + 1][j - 1]
if dp[i][j]:
if j - i + 1 > len(rlt):
rlt = s[i:j + 1]
return rlt
方法4: Manacher 算法 O(n)
参考 https://blog.csdn.net/Fly_Fly_Zhang/article/details/88780021
Manacher算法因为只有遇到没有匹配过的字符的时候,才去接着匹配,已经匹配过的字符不再匹配,所以对每个字符只进行匹配一次,所以时间复杂为O(2n + 1)=O(n)
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
if length < 2:
return s
new_s = "#"
for letter in s:
new_s += letter
new_s += "#"
Len = [0] * len(new_s)
right = 0
max_i = 0
for i in range(len(new_s)):
right = Len[max_i] + max_i - 1
if i < right:
j = 2 * max_i - i
if i + Len[j] - 1 < right:
Len[i] = Len[j]
else:
l, r = 2 * i - right, right
while l >= 0 and r < len(new_s) and l <= r and new_s[l] == new_s[r]:
l -= 1
r += 1
Len[i] = r - i
if Len[i] > Len[max_i]:
max_i = i
else:
l, r = i, i
while l >= 0 and r < len(new_s) and l <= r and new_s[l] == new_s[r]:
l -= 1
r += 1
Len[i] = r - i
if Len[i] > Len[max_i]:
max_i = i
rlt = ""
for i in range(max_i - Len[max_i] + 1, max_i + Len[max_i]):
if new_s[i] != "#":
rlt += new_s[i]
return rlt