牛客热题:最长公共子序列Ⅱ

📟作者主页:慢热的陕西人

🌴专栏链接:力扣刷题日记

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

在这里插入图片描述

牛客热题:最长公共子序列Ⅱ

题目链接

最长公共子序列(二)_牛客题霸_牛客网 (nowcoder.com)

方法一:动态规划+递归

思路

整体思路:

①先求出最长子序列的长度

②根据长度去寻找对应的字符串

详细步骤:

  • step 1:优先检查特殊情况。
  • step 2:获取最长公共子序列的长度可以使用动态规划,我们以 d p [ i ] [ j ] dp[i][j] dp[i][j]表示在s1中以 i i i结尾,s2中以 j j j结尾的字符串的最长公共子序列长度。
  • step 3:遍历两个字符串的所有位置,开始状态转移:若是 i i i位与 j j j位的字符相等,则该问题可以变成1 + d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i1][j1],即到此处为止最长公共子序列长度由前面的结果加1。
  • step 4:若是不相等,说明到此处为止的子串,最后一位不可能同时属于最长公共子序列,毕竟它们都不相同,因此我们考虑换成两个子问题, d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j] d p [ i ] [ j − 1 ] dp[i][j - 1] dp[i][j1],我们取较大的一个就可以了,由此感觉可以用递归解决。
  • step 5:但是递归的复杂度过高,重复计算了很多低层次的部分,因此可以用动态规划,从前往后加,由此形成一个表,表从位置1开始往后相加,正好符合动态规划的转移特征。
  • step 6:因为最后要返回该序列,而不是长度,所以在构造表的同时要以另一个二维矩阵记录上面状态转移时选择的方向,我们用1表示来自左上方,2表示来自左边,3表示来自上边。
  • step 7:获取这个序列的时候,根据从最后一位开始,根据记录的方向,不断递归往前组装字符,只有来自左上的时候才添加本级字符,因为这种情况是动态规划中两个字符相等的情况,字符相等才可用!

代码

string x = "";

    string ans(int i, int j, vector<vector<int>>& b)
    {
        string res = "";
        if(i == 0 || j == 0)
        {
            return res;
        }

        if(b[i][j] == 1)
        {
            res += ans(i - 1, j - 1, b);
            res += x[i - 1];
        }
        else if(b[i][j] == 2)
        {
            res += ans(i - 1, j, b);
        }
        else if(b[i][j] == 3)
        {
            res += ans(i, j - 1, b);
        }

        return res;
    }
    string LCS(string s1, string s2) 
    {
        //特殊情况:其中一个字符串为空
        if(s1.size() == 0 || s2.size() == 0)
        {
            return "-1";
        }
        int len1 = s1.size();
        int len2 = s2.size();
        x = s1;
        //dp[i][j],表示第一个字符串第i位,第二个字符串第j位为止的最长公共子序列
        vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
        //动态规划数组相加的方向
        vector<vector<int>> b(len1 + 1, vector<int>(len2 + 1, 0));
        //遍历两个字符串每个位置求得最长长度
        for(int i = 1; i <= len1; ++i)
            for(int j = 1; j <= len2; ++j)
            {
                if(s1[i - 1] == s2[j - 1])
                {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    //来自左上方
                    b[i][j] = 1;
                }
                else 
                {
                    if(dp[i - 1][j] > dp[i][j - 1])
                    {
                        dp[i][j] = dp[i - 1][j];
                        //来自左方
                        b[i][j] = 2;
                    }
                    else
                    {
                        dp[i][j] = dp[i][j - 1];
                        //来自右方
                        b[i][j] = 3;
                    }
                }
            }
        string res = ans(len1, len2, b);

        return res == "" ? "-1" : res;
    }

复杂度

  • 时间复杂度:O(n2),构造辅助数组dp与b,两层循环,递归是有方向的递归,因此只是相当于遍历了二维数组
  • 空间复杂度:O(n2),辅助二维数组dp与递归栈的空间最大为O(n2)
  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米睡不醒.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值