题目
思路
动态规划,二维表记录两个下标段间最长的回文子序列长度,然后不断扩大至整个序列
当首尾的两个字符相等时,直接将这两个字符加入:
d
p
[
0
]
[
n
−
1
]
=
d
p
[
1
]
[
n
−
2
]
+
2
dp[0][n-1] = dp[1][n-2] + 2
dp[0][n−1]=dp[1][n−2]+2
不相等时,二选一:
d
p
[
0
]
[
n
−
1
]
=
m
a
x
(
d
p
[
1
]
[
n
−
1
]
,
d
p
[
0
]
[
n
−
2
]
)
dp[0][n-1] = max(dp[1][n-1], dp[0][n-2])
dp[0][n−1]=max(dp[1][n−1],dp[0][n−2])
初始化状态
初始化时对角线全部为1,即i==j,代表只含一个字母的回文子序列
1 | 0 | 0 | 0 | 0 |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 0 |
0 | 0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 1 |
复杂度
最终要得到的数字是 d p [ 0 ] [ n − 1 ] dp[0][n-1] dp[0][n−1],循环其实是从对角线往右上角填表
-
时间复杂度
表格尺寸为 n ∗ n n*n n∗n, O ( 1 / 2 ∗ n ∗ n ) = O ( n 2 ) \mathcal{O}(1/2*n*n)=\mathcal{O}(n^2) O(1/2∗n∗n)=O(n2) -
空间复杂度
表格尺寸为 n ∗ n n*n n∗n, O ( 1 / 2 ∗ n ∗ n ) = O ( n 2 ) \mathcal{O}(1/2*n*n)=\mathcal{O}(n^2) O(1/2∗n∗n)=O(n2)
代码
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
# dp[i][j]表示从i到j之间的最长回文长度
dp = [[0 for _ in range(n)] for _ in range(n)]
# 初始化,对角线为1,即单个字符的回文长度为1
for i in range(n):
dp[i][i] = 1
# 从长度为2开始扩大(从对角线向右上角填表)
for length in range(2, n + 1):
# 从前到后查看长度为length的子串,检查区间首尾字符是否相同
for i in range(n - length + 1):
j = i + length - 1 # j永远比i大,所以填的是右上角区域
if s[i] == s[j]: # 相同,+2
dp[i][j] = 2 + (0 if length == 2 else dp[i+1][j-1])
else: # 不相同,从规模更小的字符串中得出
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
return dp[0][n-1] # 右上角数值