給定乙個字串 s,找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba" 也是乙個有效答案。
示例 2:
輸入: "cbbd"
輸出: "bb"
2.1 動態規劃
對於乙個子串而言,如果它是回文串,並且長度大於 2,那麼將它首尾的兩個字母去除之後,它仍然是個回文串。例如對於字串「ababa」,如果我們已經知道 「bab」 是回文串,那麼 「ababa」 一定是回文串,這是因為它的首尾兩個字母都是「a」。
根據這樣的思路,我們就可以用動態規劃的方法解決本題。我們用p(i,j)
表示字串s
的第i
到j
個字母組成的串(下文表示成s[i:j]
)是否為回文串:
p (i
,j)=
\text & 如果子串 s_ \ldots s_ 是回文串 \\ \text & 其它情況 \end\right.
p(i,j)
===s_\right)
p(i,j)
=p(i
+1,j
−1)∧
(si
==sj
)也就是說,只有 s[i+1:j−1] 是回文串,並且 s 的第 i 和 j 個字母相同時,s[i:j] 才會是回文串。
上文的所有討論是建立在子串長度大於 2 的前提之上的,我們還需要考慮動態規劃中的邊界條件,即子串的長度為 1 或 2。對於長度為 1 的子串,它顯然是個回文串;對於長度為 2 的子串,只要它的兩個字母相同,它就是乙個回文串。因此我們就可以寫出動態規劃的邊界條件:
p(i, i)=\text \\ p(i, i+1)=\left(s_==s_\right) \end\right.
中 j-i+1
p(i,j)
=true中j
−i+1
(即子串長度)的最大值。
**注意:**在狀態轉移方程中,我們是從長度較短的字串向長度較長的字串進行轉移的,因此一定要注意動態規劃的迴圈順序。
class
solution
:def
longestpalindrome
(self, s:
str)
->
str:
n =len(s)
dp =[[
false
]* n for _ in
range
(n)]
ans =
""# 列舉子串的長度 l+1
for l in
range
(n):
# 列舉子串的起始位置 i,這樣可以通過 j=i+l 得到子串的結束位置
for i in
range
(n):
j = i + l
if j >=
len(s)
:break
if l ==0:
dp[i]
[j]=
true
elif l ==1:
dp[i]
[j]=
(s[i]
== s[j]
)else
: dp[i]
[j]=
(dp[i +1]
[j -1]
and s[i]
== s[j]
)if dp[i]
[j]and l +
1>
len(ans)
: ans = s[i:j+1]
return ans
複雜度分析:
時間複雜度:o(n
2)
o(n^2)
o(n2
),其中 n 是字串的長度。動態規劃的狀態總數為 o(n
2)
o(n^2)
o(n2
),對於每個狀態,我們需要轉移的時間為 o(1)。
空間複雜度 o(n
2)
o(n^2)
o(n2
),即儲存動態規劃狀態需要的空間。
2.2 中心擴充套件法
觀察一下方法一中的狀態轉移方程:
p(i, i) & =\text \\ p(i, i+1) & =\left(s_==s_\right) \\ p(i, j) & =p(i+1, j-1) \wedge\left(s_==s_\right) \end\right.
⎩⎨⎧p(
i,i)
p(i,
i+1)
p(i,
j)=
true =(
si=
=si+
1)=
p(i+
1,j−
1)∧(
si=
=sj
)其中的狀態轉移鏈:
p (i
,j)←
p(i+
1,j−
1)←p
(i+2
,j−2
)←⋯←
某一邊界情況
p(i, j) \leftarrow p(i+1, j-1) \leftarrow p(i+2, j-2) \leftarrow \cdots \leftarrow \text
p(i,j)
←p(i
+1,j
−1)←
p(i+
2,j−
2)←⋯
←某一邊界情況
可以發現,所有的狀態在轉移的時候的可能性都是唯一的。也就是說,我們可以從每一種邊界情況開始「擴充套件」,也可以得出所有的狀態對應的答案。
邊界情況即為子串長度為 1 或 2 的情況。我們列舉每一種邊界情況,並從對應的子串開始不斷地向兩邊擴充套件。如果兩邊的字母相同,我們就可以繼續擴充套件,例如從 p(i+1,j−1) 擴充套件到 p(i,j);如果兩邊的字母不同,我們就可以停止擴充套件,因為在這之後的子串都不能是回文串了。
class
solution
:def
expandaroundcenter
(self, s, left, right)
:while left >=
0and right <
len(s)
and s[left]
== s[right]
: left -=
1 right +=
1return left +
1, right -
1def
longestpalindrome
(self, s:
str)
->
str:
start, end =0,
0for i in
range
(len
(s))
: left1, right1 = self.expandaroundcenter(s, i, i)
left2, right2 = self.expandaroundcenter(s, 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^2)
o(n2
),其中 n 是字串的長度。長度為 1 和 2 的回文中心分別有 n 和 n−1 個,每個回文中心最多會向外擴充套件 o(n) 次。
空間複雜度:o(1)。
2.3 manacher 演算法
manacher 演算法的複雜度為 o(n)
時間複雜度:o(n),其中 n 是字串的長度。由於對於每個位置,擴充套件要麼從當前的最右側臂長 right 開始,要麼只會進行一步,而 right 最多向前走 o(n) 步,因此演算法的複雜度為 o(n)。
空間複雜度:o(n),我們需要 o(n) 的空間記錄每個位置的臂長。
參考:leetcode官方題解
LeetCode5最長回文子串
給定乙個字串s,找到s中最長的回文子串。你可以假設s長度最長為1000。示例 輸入 babad 輸出 bab 注意 aba 也是有效答案示例 輸入 cbbd 輸出 bb 動態規劃來做,每個回文字串的子字串也是回文字串,即string是回文字串那麼它的string.substring 1,lenth ...
LeetCode 5 最長回文子串
問題描述 給定乙個字串s,找到s中最長的回文子串。你可以假設s的最大長度為1000。示例 1 輸入 babad 輸出 bab 注意 aba 也是乙個有效答案。示例 2 輸入 cbbd 輸出 bb 解決方案 中心擴充套件演算法 事實上,只需使用恆定的空間,我們就可以在 o n 2 的時間內解決這個問題...
leetcode5 最長回文子串
遞推式 1 一般 s i 1 s j 1 and j i and j i len s i 1,j 1 2 初始化dp矩陣對角線的值為 true,相鄰兩個元素相等時dp i i 1 為true 初始化回文串起始位置和長度。def longestpalindrome s n len s if s ret...