描述
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
示例 1:
输入:
“bbbab”
输出:
4
一个可能的最长回文子序列为 “bbbb”。
示例 2:
输入:
“cbbd”
输出:
2
一个可能的最长回文子序列为 “bb”。
提示:
1 <= s.length <= 1000
s 只包含小写英文字母
思路
还是用动态规划,枚举所有情况。dp[i][j]
定义为字符串s的第i个字符到第j个字符之间最长的回文子序列长度。(哎…这个也不是很好想到啊)
初始值:当i>j时,没有意义,所以全部置为0;当i=j时,即每个字符的最长回文子序列就是自己,置为1。
要求dp[i][j]
,如果知道了dp[i + 1][ j - 1]
的值,那么判断一下s[i]
和s[j]
的字符是否相等,如果相等,那么从i到j的最长回文子序列长度就是在dp[i + 1][j - 1]
的基础上+2;若两者不想等呢?那么最少有一个不属于[i , j]区间中的最长回文子序列中,把s[i]和s[j]放入,哪个字符放入后回文子序列长度更长就取哪个值,即dp[i][j] = max{dp[i][j - 1], dp[i + 1][j])}
,至此,状态转移方程就得到了。
下面用图表示一下dp数组:
由图可以看出,未知的红色格子的dp值只与它左边、下边、斜左下方的格子有关。即dp[i][j]
只与dp[i - 1][j]
和dp[i][j - 1]
、dp[i + 1][j]
有关。所以可以按照下图的顺序斜着进行遍历:
或者可以从右下角开始,从左至右遍历:
解答
class Solution {
public:
int longestPalindromeSubseq(string s) {
int size = s.size();
//dp[][]表示从第i个到第j个字符间最大的回文子序列长度
vector<vector<int> > dp(size, vector<int>(size, 0));
//初始化
for(int i = 0;i < size; ++i)
dp[i][i] = 1;
int i = 0, j = 1, k = 1;
for(;j < size;){
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]);
i ++;
j ++;
if(j == size){
i = 0;k ++;j = k;
}
}
return dp[0][size-1];
}
};