1. 题目来源
链接:516. 最长回文子序列
类似:
2. 题目解析
区间dp 一道不错的练手题。
思路:
- 状态定义:f[i][j]:在 s[i]~s[j] 中最长回文子序列的最大长度。
- 状态转移:也是从 s[i]、s[j] 选不选的角度考虑问题的。
- s[i]、s[j] 都选:如果 s[i] == s[j] 那么 f[i][j] 将由 f[i+1][j-1]+2 转移过来
- s[i] 选 s[j] 不选:这里用 f[i][j-1] 转移
- 同理,LCS 问题,这里的 f[i][j-1] 转移,并不保证
i
一定被选,但是它却是包含上述的状态定义的。
- 同理,LCS 问题,这里的 f[i][j-1] 转移,并不保证
- s[i] 不选 s[j] 选:这里用 f[i+1][j] 转移
- 同理,LCS 问题的,这里的 f[i+1][j] 转移,并不能保证
j
一定被选,但是它却也是包含上述状态定义的。
- 同理,LCS 问题的,这里的 f[i+1][j] 转移,并不能保证
- s[i]、s[j] 都不选:这里用 f[i+1][j-1] 转移。
- 这里的状态已经被 f[i+1][j] 和 f[i][j-1] 的状态完全包含了。所以状态转移的时候,这个可以不出现。
本题是个很经典的区间 dp 问题,采用了 LCS 的状态转移讨论方式。
二维 dp 下,不一定需要两个字符串,只需要有两个状态点,加上 选/不选 的问题下,就能 LCS 的状态细分、讨论的手段。
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( n 2 ) O(n^2) O(n2)
代码:
常规写法:
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n = s.size();
int f[n][n]; memset(f, 0, sizeof(f));
for (int len = 1; len <= n; len ++ )
for (int i = 0; i + len - 1 < n; i ++ ) {
int j = i + len - 1;
if (i == j) f[i][j] = 1;
else {
if (s[i] == s[j]) f[i][j] = max(f[i][j], f[i + 1][j - 1] + 2);
else f[i][j] = max(f[i + 1][j], f[i][j - 1]);
}
}
return f[0][n - 1];
}
};