LeetCode 5 最長回文子串

2021-10-24 05:16:43 字數 4347 閱讀 6623

給定乙個字串 s,找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。

示例 1:

輸入: "babad"

輸出: "bab"

注意: "aba" 也是乙個有效答案。

示例 2:

輸入: "cbbd"

輸出: "bb"

2.1 動態規劃

對於乙個子串而言,如果它是回文串,並且長度大於 2,那麼將它首尾的兩個字母去除之後,它仍然是個回文串。例如對於字串「ababa」,如果我們已經知道 「bab」 是回文串,那麼 「ababa」 一定是回文串,這是因為它的首尾兩個字母都是「a」。

根據這樣的思路,我們就可以用動態規劃的方法解決本題。我們用p(i,j)表示字串s的第ij個字母組成的串(下文表示成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...