5. 最长回文子串
Posted 炫云云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5. 最长回文子串相关的知识,希望对你有一定的参考价值。
5. 最长回文子串
给你一个字符串 s
,找到 s
中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
动态规划
对于一个子串而言,如果它是回文串,并且长度大于 2 , 那么将它首尾的两个字母去除之后,它仍 然是个回文串。例如对于字符串 “ababa”, 如果我们已经知道 “bab" 是回文串,那么 "ababa"一定 是回文串, 这是因为它的首尾两个字母都是 “a”。
第 1 步:定义状态
根据这样的思路,我们就可以用动态规划的方法解决本题。我们用
d
P
(
i
,
j
)
dP(i, j)
dP(i,j) 表示字符串
s
s
s 的第
i
i
i 到
j
j
j 个字母组成的串 (下文表示成
s
[
i
:
j
]
)
s[i: j])
s[i:j]) 是否为回文串:
d
P
(
i
,
j
)
=
{
true,
如果子串
S
i
…
S
j
是回文串
false,
其它情况
dP(i, j)= \\begin{cases}\\text { true, } & \\text { 如果子串 } S_{i} \\ldots S_{j} \\text { 是回文串 } \\\\ \\text { false, } & \\text { 其它情况 }\\end{cases}
dP(i,j)={ true, false, 如果子串 Si…Sj 是回文串 其它情况
这里的「其它情况」包含两种可能性:
- s [ i , j ] s[i, j] s[i,j] 本身不是一个回文串;
- i > j i>j i>j, 此时 s [ i , j ] s[i, j] s[i,j] 本身不合法。
第 2 步:思考状态转移方程
那么我们就可以写出动态规划的状态转移方程:
d
P
(
i
,
j
)
=
d
P
(
i
+
1
,
j
−
1
)
a
n
d
(
S
i
=
=
S
j
)
dP(i, j)=dP(i+1, j-1)~and ~\\left(S_{i}==S_{j}\\right)
dP(i,j)=dP(i+1,j−1) and (Si==Sj)
也就是说,只有
s
[
i
+
1
:
j
−
1
]
s[i+1: j-1]
s[i+1:j−1] 是回文串,并且
s
s
s 的第
i
i
i 和
j
j
j 个字母相同时,
s
[
i
:
j
]
s[i: j]
s[i:j] 才会是回文串。
说明:
- 「动态规划」的「自底向上」求解问题的思路,很多时候是在填写一张二维表格。由于
s[i..j]
表示s
的一个子串,因此i
和j
的关系是i <= j
,只需要填这张表格对角线以上的部分; - 看到
dp[i + 1][j - 1]
就需要考虑特殊情况:如果去掉s[i..j]
头尾两个字符子串s[i + 1..j - 1]
的长度严格小于 2(不构成区间),即 j − 1 − ( i + 1 ) + 1 < 2 j - 1 - (i + 1) + 1 < 2 j−1−(i+1)+1<2 时,整理得 j − i < 3 j - i < 3 j−i<3, 此时s[i..j]
是否是回文只取决于s[i]
与s[j]
是否相等。
第 3 步:考虑输出
上文的所有讨论是建立在子串长度大于 2 的前提之上的,我们还需要考虑动态规划中的边界条件, 即子串的长度为 1 或 2 。
- 对于长度为 1 的子串,它显然是个回文串;
- 对于长度为 2 的子串,只要它 的两个字母相同,它就是一个回文串。
根据这个思路,我们就可以完成动态规划了,最终的答案即为所有 d P ( i , j ) = dP(i, j)= dP(i,j)= true 中 j − i + 1 j-i+1 j−i+1 (即子 串长度) 的最大值。注意:在状态转移方程中,我们是从长度较短的字符串向长度较长的字符串进 行转移的,因此一定要注意动态规划的循环顺序。
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n < 2:
return s
max_len = 1
begin = 0
# dp[i][j] 表示 s[i..j] 是否是回文串
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = True
# 递推开始
# 注意:先填左下角.填表规则:先一列一列的填写
# 枚举右边界
for j in range(n):
# 确定左边界
for i in range(j+1):
if s[i] != s[j]:
dp[i][j] = False
else:
if j - i < 3: #相等的情况下
dp[i][j] = True #考虑头尾去掉以后没有字符剩余,或者剩下一个字符的时候,肯定是回文串
else:
dp[i][j] = dp[i + 1][j - 1]
# 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if dp[i][j] and j - i + 1 > max_len:
max_len = j - i + 1
begin = i
return s[begin:begin + max_len]
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
dp = [[0] * n for _ in range(n)]
res = ""
for i in range(n):
for j in range(i + 1):
if s[i] == s[j] and (i - j < 3 or dp[j+1][i-1]):
dp[j][i] = 1
res = max(res, s[j:i+1], key=len)
return res
参考
以上是关于5. 最长回文子串的主要内容,如果未能解决你的问题,请参考以下文章