给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。
示例 1:
输入:
“bbbab”
输出:
4
一个可能的最长回文子序列为 “bbbb”。
示例 2:
输入:
“cbbd”
输出:
2
一个可能的最长回文子序列为 “bb”。
分析:
- 这道题如果能想到定义
dp[i][j]表示s[i:j+1]的最长回文子序列长度
的话就很简单了,所以在这里的话,我更想去说一下我是怎么想到要用dp以及这样来定义dp的,我认为这才是有帮助的 - 看到示例1的时候,’bbbab’,我想首先依次遍历,然后看此时遍历的字符有没有在之前出现过,如果出现过,以这个两个相同字符为边界,看是否能找到一个回文串,那么中间的字符是什么状态呢?是本身就是回文串呢或者说其子串包含回文串呢,想到这一步我突然意识到,这不就是问题的解依赖于其子问题的解吗,于是想到用dp来解决
- 那么怎么定义dp呢,因为按照我的思路是固定住两边观察中间,所以我定义一个二维dp,那么上面说的那种情况就可以写成
dp[i][j] = dp[i+1][j-1] + 2
,到这里豁然开朗,我知道自己没想错……
思路:
if s[i] == s[j]: dp[i][j] = dp[i+1][j-1] + 2
else: dp[i][j] = max(dp[i+1][j], dp[i][j-1])
- 我是用斜向遍历的,对于示例1:
- (0,1)->(1,2)->(2,3)->(3,4)
- (0,2)->(1,3)->(2,4)
- (0,3)->(2,4)
- (0,4) # 最后结果
- 实际上你可以用从最后一层向上遍历,那样的代码应该是最优美的!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Solution(object):
def longestPalindromeSubseq(self, s):
"""
:type s: str
:rtype: int
"""
# dp[i][j] reps from i to j,the length of LPS
if s == s[::-1]: return len(s) # this code helps my runtime decrease from 1500ms to 500ms,beats 99%
n = len(s)
dp = [[1]*n for _ in range(n)]
# 处理斜向遍历时i,j的间隔
gap = 1
for index in range(n-1):
i,j = 0,gap
while j < n:
if gap == 1:
dp[i][j] += s[i] == s[j]
elif s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
else:
dp[i][j] = max(dp[i+1][j],dp[i][j-1])
i += 1
j += 1
gap += 1
return dp[0][n-1]# 返回dp中最大的数,实际也就是这个