今日份题目:
给你一个字符串 s
,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例1
输入:s = "bbbab" 输出:4 解释:一个可能的最长回文子序列为 "bbbb" 。
示例2
输入:s = "cbbd" 输出:2 解释:一个可能的最长回文子序列为 "bb" 。
提示
-
1 <= s.length <= 1000
-
s
仅由小写英文字母组成
题目思路
动态规划,使用二维dp数组记录[i,j]间的最大回文子序列长度。
开始的时候,我尝试使用reverse ( s.begin ( ) , s.end ( ) )翻转字符串判断连续相同位置的最大长度,最后发现与这道题不同,这道题目允许跨某些字母满足回文,所以放弃原来的想法,不过翻转后再动态规划或许也可以,欢迎感兴趣的朋友试一下,评论区讨论哦!
接下来继续讲这道题目的思路。
因为需要枚举区间内的最大长度,如果从前往后遍历,开始的区间跨度太大,可以理解为第一次判断就得出最后返回的结果,肯定是不对的,所以是从最后位置开始遍历i,j就是i+1到最后。然后,回文要满足两头的字母一样,所以对遍历出的每组i和j判断字符是否相同,相同的话,就在原长度也就是i+1到j-1范围内的最大长度加上这两个点的长度2;如果不同,当前点就更新为区间内最大长度。最后返回0到长度-1范围内的最大长度即可。
接下来是比较关键和大家比较关心的状态转移方程:
if(i、j处的字符相同)
{dp[i][j]=dp[i+1][j-1]+2;//在原来长度的基础上加上这两个字符的长度
}
else 两头的字符不同
{dp[i][j]=max(dp[i+1][j],dp[i][j-1]);//不需要加上这两头的字符,根据相邻范围的值更新
}
代码
class Solution
{
public:int longestPalindromeSubseq(string s) {int n=s.size();int dp[2000][2000]={0};//表示[i,j]范围内的最长回文子序列的长度for(int i=n-1;i>=0;i--) {dp[i][i]=1;//单独一个字符,自己就是最长回文子序列char c1=s[i];for(int j=i+1;j<n;j++) {char c2=s[j];if(c1==c2) //两头的字符相同{dp[i][j]=dp[i+1][j-1]+2;//在原来长度的基础上加上这两个字符的长度} else //两头的字符不同{dp[i][j]=max(dp[i+1][j],dp[i][j-1]);//不需要加上这两头的字符}}}return dp[0][n-1];//找到[0,n-1]的整个字符串中的最长回文子序列长度}
};
提交结果
欢迎大家在评论区讨论,如有不懂的代码部分,欢迎在评论区留言!