题目
题意
求数组的回文子序列的最长长度
分析
用
f[i][j]
表示从i到j的回文子序列的最长长度
转移方程如下
如果
s[i]==s[j]
,
f[i][j]=f[i+1][j−1]+2
如果
s[i]!=s[j]
,
f[i][j]=max(f[i][j−1],f[i+1][j])
可以理解为每次头尾扩充1个位置, 即i, j为扩充的位置, 那么涉及的子串就包括
f[i+1][j−1]
(不包括两个扩充的位置),
f[i][j−1]
(只包括i)和
f[i+1][j]
(只包括j)
当
i==j
时 ,
s[i]
和
s[j]
可以在
f[i+1][j−1]
的基础上再加上2个字符的长度;
否则, 考虑只扩充i或只扩充j的子问题, 即
max(f[i][j−1],f[i+1][j])
一开始我是将长度len作为外层循环, 然后里层循环是所有len的可能,代码如下
class Solution {
public:
int longestPalindromeSubseq(string s) {
vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
for (int len = 2; len <= s.size(); len++) {
for (int left = 0; left <= s.size()-len; left++) {
int right = left+len-1;
int temp = max(dp[left][right-1], dp[left+1][right]);
if (s[left] == s[right])
dp[left][right] = max(dp[left+1][right-1] + 2,temp);
else
dp[left][right] = max(dp[left+1][right-1], temp);
}
}
return dp[0][s.size()-1];
}
};
但是这个开销太大了(118 ms), 按这个思路分析,
要求
dp[i][j]
(图中棕色位置的时候)
只需要知道蓝色区域的子问题结果, 所以将循环改成
- 外层为i从下往上
- 内层为j从左往右
代码如下:
class Solution {
public:
int longestPalindromeSubseq(string s) {
vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
for (int i = s.size()-1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i+1; j < s.size(); j++) {
if(s[i] == s[j])
dp[i][j] = dp[i+1][j-1]+2;
else
dp[i][j] = max(dp[i][j-1], dp[i+1][j]);
}
}
return dp[0][s.size()-1];
}
};