动态规划——最长回文子序列

问题来源:leetcode 516

问题定义

给定一个字符串 s,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000

示例 1:

输入:

"bbbab"
输出:

4
一个可能的最长回文子序列为 "bbbb"。

示例 2:

输入:

"cbbd"
输出:

2
一个可能的最长回文子序列为 "bb"。

提示:

  • 1 <= s.length <= 1000
  • s 只包含小写英文字母

转换成最长公共子序列问题

最长公共子序列

首先将字符串反转,然后求这两个字符串的最长公共子序列便是该字符串的最长回文子序列。

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        string s_reverse = s;
        reverse(s_reverse.begin(), s_reverse.end());
        return lcss(s, s_reverse);
    }

    int lcss(string& s1, string& s2) {
        int n = s1.size(), m = s2.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1));
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=m; j++) {
                if(s1[i-1] == s2[j-1]) {
                    dp[i][j] = dp[i-1][j-1] + 1;
                } else {
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
                }
            }
        }
        return dp[n][m];
    }
};

直接动态规划分析

优化子结构:设原问题的字符串 S = s 1 s 2 , . . . s n S=s_1s_2,...s_n S=s1s2,...sn 的最长回文子序列为 X = x 1 , x 2 , . . . , x m X=x_1,x_2,...,x_m X=x1,x2,...,xm

  • 如果 s 1 = s n s_1 = s_n s1=sn,那么 X ′ = x 2 , . . . x n − 1 X^{'}=x_2,...x_{n-1} X=x2,...xn1 是子问题的字符串 S ′ = s 2 , . . . , s n − 1 S^{'}=s_2,...,s_{n-1} S=s2,...,sn1 的最长回文子序列,即需要求解子问题 S ′ S^{'} S 的最优解 X ′ X^{'} X。反证法证明:如果 X ′ X^{'} X 不是 S ′ S^{'} S 的最优解,那么 S ′ S^{'} S 存在一个更长的回文子序列,在此基础上再加上 s 1 s_1 s1 s n s_n sn,可以得到 S S S 的一个更长的回文子序列,与 X X X S S S 的一个最长的回文子序列矛盾。
  • 如果 s 1 ≠ s n s_1 \neq s_n s1=sn,那么 X X X 是子问题 S 1 ′ = s 1 , . . . , s n − 1 S^{'}_1=s_1,...,s_{n-1} S1=s1,...,sn1 S 2 ′ = s 2 , . . . , s n S^{'}_{2}=s_2,...,s_n S2=s2,...,sn 的最长回文子序列中较长的那个,即需要求解子问题 S 1 ′ S^{'}_1 S1 S 2 ′ S^{'}_2 S2 的最优解,同样可以通过反证法来证明。

重叠子问题:简单地通过递归算法来计算,会有子问题被重复计算。

递归地定义最优解的值:设 d p ( i , j ) dp(i,j) dp(i,j) 表示以 s i s_i si 开头,以 s j s_j sj 结尾的字符串的最长回文子序列的长度,那么:

  • 如果 i > j i > j i>j,那么 d p ( i , j ) = 0 dp(i, j)=0 dp(i,j)=0
  • 如果 i = j i = j i=j,那么 d p ( i , j ) = 1 dp(i,j) = 1 dp(i,j)=1;
  • 如果 i < j i < j i<j,那么:
    • 如果 s i = s j s_i = s_j si=sj,则 d p ( i , j ) = 2 + d p ( i + 1 , j − 1 ) dp(i,j) = 2 + dp(i+1, j-1) dp(i,j)=2+dp(i+1,j1)
    • 否则, d p ( i , j ) = m a x {   d p ( i + 1 , j ) ,   d p ( i , j − 1 )   } dp(i,j) = max\{\ dp(i+1, j),\ dp(i, j-1)\ \} dp(i,j)=max{ dp(i+1,j), dp(i,j1) }
class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n));
        for(int i=0; i<n; i++) {
            dp[i][i] = 1;
        }
        for(int l = 2; l<=n; l++) {
            for(int i = 0; i<= n-l; i++) {
                int j = i + l - 1;
                if(s[i] == s[j]) {
                    dp[i][j] = 2 + dp[i+1][j-1];
                } else {
                    dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
                }
            }
        }
        return dp[0][n-1];
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值