题意:
- 给定一个字符串,找到其中最长的回文子序列,并返回该序列的长度。
方法一:动态规划
解题思路:
- 求解最长回文子序列,有明显的子问题重叠,使用动态规划,考虑以下最优子结构:
(1)dp[i][j]-----序列s[i]-->s[j]的最长回文子序列的长度。
(2)dp[i][i]=1
(3)dp[i][j]=0(j<i)
(4)dp[i][j]=dp[i+1][j-1]+2 (if s[i]==s[j])
(5)dp[i][j]=max(dp[i+1,j],dp[i,j-1]) (if s[i]!=s[j]) - (2)显然,(3)是对(4)的边界条件的兼容,(4)易得,(5)由于s[i]!=s[j],那么s[i]和s[j]最多只有一个能加入到最长回文子序列中,因此从两者中取最大值。
图解如下:
代码如下:
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param s string 一个字符串由小写字母构成,长度小于5000 * @return int */ int longestPalindromeSubSeq(string s) { // write code here int n=s.size(); vector<vector<int>> dp(n,vector<int>(n)); //i为起始索引,j为结束索引 for(int i=n-1;i>=0;i--){ dp[i][i]=1; for(int j=i+1;j<n;j++){ //相同,则加2 if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2; //不同,则从dp[i+1][j] dp[i][j-1]中寻找最大值。 else dp[i][j]=max(dp[i+1][j],dp[i][j-1]); } } return dp[0][n-1]; } };
复杂度分析:
时间复杂度:,对数组dp的遍历更新,遍历次数为n^2/2,故。
空间复杂度:,二维数组dp。
方法二:
解题思路:
- 方法一中的二维数组dp,我们通过图解和代码发现,每一个只依赖于,,。因此,我们只需要一维数组即可。
- 同时由于依赖于,因此i需要从大往小遍历。
代码如下:
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param s string 一个字符串由小写字母构成,长度小于5000 * @return int */ int longestPalindromeSubSeq(string s) { // write code here int n=s.size(); vector<int> dp(n,1); //i为起始索引,j为结束索引 for(int i=n-1;i>=0;i--){ int pre=0; for(int j=i+1;j<n;j++){ //临时变量存储dp[j],需要更新为下一轮遍历的pre int tmp=dp[j]; //相同,则加2 if(s[i]==s[j]) dp[j]=pre+2; //不同,则从dp[j] dp[j-1]中寻找最大值。更新到dp[j] else dp[j]=max(dp[j],dp[j-1]); pre=tmp; } } return dp[n-1]; } };
复杂度分析:
时间复杂度:,同上。
空间复杂度:,一位数组dp,维度为n。