第五章 - 高频考题(中等)
1906. 查询差绝对值的最小值
0516. 最长回文子序列

题目地址(516. 最长回文子序列)

题目描述

1
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
2
3
4
5
示例 1:
6
输入:
7
8
"bbbab"
9
输出:
10
11
4
12
一个可能的最长回文子序列为 "bbbb"。
13
14
示例 2:
15
输入:
16
17
"cbbd"
18
输出:
19
20
2
21
一个可能的最长回文子序列为 "bb"。
22
23
24
25
提示:
26
27
1 <= s.length <= 1000
28
s 只包含小写英文字母
Copied!

前置知识

  • 动态规划

公司

  • 阿里
  • 腾讯
  • 百度
  • 字节

思路

这是一道最长回文的题目,要我们求出给定字符串的最大回文子序列。
516.longest-palindromic-subsequence-1
解决这类问题的核心思想就是两个字“延伸”,具体来说
  • 如果一个字符串是回文串,那么在它左右分别加上一个相同的字符,那么它一定还是一个回文串,因此回文长度增加2
  • 如果一个字符串不是回文串,或者在回文串左右分别加不同的字符,得到的一定不是回文串,因此回文长度不变,我们取[i][j-1]和[i+1][j]的较大值
516.longest-palindromic-subsequence-2
事实上,上面的分析已经建立了大问题和小问题之间的关联, 基于此,我们可以建立动态规划模型。
我们可以用 dp[i][j] 表示 s 中从 i 到 j(包括 i 和 j)的回文序列长度, 状态转移方程只是将上面的描述转化为代码即可:
1
if (s[i] === s[j]) {
2
dp[i][j] = dp[i + 1][j - 1] + 2;
3
} else {
4
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
5
}
Copied!
base case 就是一个字符(轴对称点是本身)
516.longest-palindromic-subsequence-3

关键点

  • ”延伸“(extend)

代码

代码支持:JS,Python3
JS Code:
1
/*
2
* @lc app=leetcode id=516 lang=javascript
3
*
4
* [516] Longest Palindromic Subsequence
5
*/
6
/**
7
* @param {string} s
8
* @return {number}
9
*/
10
var longestPalindromeSubseq = function (s) {
11
// bbbab 返回4
12
// tag : dp
13
const dp = [];
14
15
for (let i = s.length - 1; i >= 0; i--) {
16
dp[i] = Array(s.length).fill(0);
17
for (let j = i; j < s.length; j++) {
18
if (i - j === 0) dp[i][j] = 1;
19
else if (s[i] === s[j]) {
20
dp[i][j] = dp[i + 1][j - 1] + 2;
21
} else {
22
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
23
}
24
}
25
}
26
27
return dp[0][s.length - 1];
28
};
Copied!
Python3 Code(记忆化递归):
1
class Solution:
2
def longestPalindromeSubseq(self, s: str) -> int:
3
@cache
4
def dp(l,r):
5
if l >= r: return int(l == r)
6
if s[l] == s[r]:
7
return 2 + dp(l+1,r-1)
8
return max(dp(l+1, r), dp(l, r-1))
9
return dp(0, len(s)-1)
Copied!
Python3 Code(普通 dp)
1
class Solution:
2
def longestPalindromeSubseq(self, s: str) -> int:
3
n = len(s)
4
dp = [[0]*n for _ in range(n)]
5
6
for i in range(n-1, -1, -1):
7
for j in range(i, n):
8
if i == j:
9
dp[i][j] = 1
10
elif s[i] == s[j]:
11
dp[i][j] = dp[i+1][j-1]+2
12
else:
13
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
14
return dp[0][-1]
Copied!
复杂度分析
  • 时间复杂度:枚举所有的状态需要 n^2 时间,状态转移需要常数的时间,因此总的时间复杂度为 $O(n^2)$
  • 空间复杂度:我们使用二维 dp 存储所有状态,因此空间复杂度为 $O(n^2)$

相关题目

大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。 大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。