最长回文子串
META
- URL: Leetcode.05 最长回文子串
- 难度: 中等
- 分类: 动态规划,字符串
描述
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
回文就是倒着念正着念都一样的,如aba
倒过来念还是aba
。
动态规划
一看这道题目就想起了动态规划。
对于子字符串s[i][j]
,如果s[i]==s[j]
,那么就看被ij包裹的里面的内容是不是回文,这就构成了递归式。
t(i,j) = s[i]==s[j] and t(i+1,j-1)
有递归就得有边界条件,如果i和j中间有一个字符或者没字符,那么就仅当s[i]==s[j]
时就是回文啦,比如aba
,aa
。所以可以推出个条件:j-i<3
的时候,结果是True
然后我们使用动态规划,需要构造一个二维数组t存储字符串是否是回文的状态。
class Solution:
def longestPalindrome(self,s: str) -> str:
size = len(s)
t = [[False for j in range(size)] for i in range(size)]
ml = 1
si = 0
for j in range(1,size):
for i in range(j):
if s[i] == s[j]:
if j - i < 3:
t[i][j] = True
else:
t[i][j] = t[i+1][j-1]
if t[i][j]:
cl = j - i + 1
if cl > ml:
ml = cl
si = i
return s[si:si+ml]
时间复杂度O(n2),空间复杂度O(n2)
中心扩展法
此法时间复杂度也是O(n^2),只是空间复杂度变成了常数级别。
思路是对于字符串中的每一个字符,认为它是回文串的中心,那么只需查找两边的一样字符串的长度就可以确定以它为中心的回文串的长度。然后去找最大的一个。
class Solution:
def longestPalindrome(self,s: str)->str:
start,length = 0 ,0
for i in range(len(s)):
curLen = max(
self.getPalindromeLen(s,i,i),
self.getPalindromeLen(s,i,i+1)
)
if curLen > length:
start = i - (curLen-1) // 2
length = curLen
return s[start:start+length]
def getPalindromeLen(self,s: str,l:int,r:int)->int:
while l>=0 and r<len(s) and s[l] == s[r]:
l = l - 1
r = r + 1
return r - l - 1
需要注意的就是,因为双数长度的字符串的中心并没有字符,或者说在两个字符中间,比如aabb
的中心是aa#bb
,井号的位置。在算法中我们认为中间的两个字符是该字符串的中心。所以对于每个位置,要判断两次,一个是以当前位置为中心,一个是以当前位置和后一个字符为中心。