2021-5-21最长公共子序列

主题:

日常学习记录,包括:

  • 算法题:最长公共序列
    参考详细讲解最长公共序列
    内容:
    由于认为这个问题十分典型,并且比较难,所以进行单独列出来>
    问题描述:
    给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长
    公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列

最长公共序列,首先要知道什么叫序列。一个数组的序列表示从中抽出一些元素,这列元素在原来的
数组中是按照从前到后排列的,但是不必是连续的。和子序列是有区别的。子序列要求必须是
连续的。
解决LCS问题,需要把原问题分解成若干个子问题,所以需要刻画LCS的特征。

设A=“a0,a1,…,am”,B=“b0,b1,…,bn”,且Z=“z0,z1,…,zk”为它们的最长公共子序列。不难证明有以下性质:
如果am=bn,则zk=am=bn,且“z0,z1,…,z(k-1)”是“a0,a1,…,a(m-1)”和“b0,b1,…,b(n-1)”的一个最长公共子序列;
如果am!=bn,则若zk!=am,蕴涵“z0,z1,…,zk”是“a0,a1,…,a(m-1)”和“b0,b1,…,bn”的一个最长公共子序列;
如果am!=bn,则若zk!=bn,蕴涵“z0,z1,…,zk”是“a0,a1,…,am”和“b0,b1,…,b(n-1)”的一个最长公共子序列。
也就是说,当am!=bn 的时候,一定可以转化为a0 …a(m-1) 和b0 …b(n) 或者 a0 …a(m) 和b0 …b(n-1)
一定可以减少一个元素,相当于有两个子情况,对于这个位置上最大的LCS,相当于是max(dp[i-1][j],dp[i][j-1])

此外还有一个技巧:可以设置dp[len(str1)+1, len(str2)+1]
这个时候,可以不需要进行初始化第0行和第0列。因为此时已经是0,并且是不对应有元素的
这样虽然多了一些空间,程序可以更加简化。取消特殊处理。
这样也可以理解为表示的是空字符串。
不过这样要注意:dp[i][j]对应的字符串是s1[i-1]和s2[j-1]

得到了dp表格,还需要进行追溯。

代码:


string LCS(string s1, string s2) {
        // write code here
        int len1 = s1.size();
        int len2 = s2.size();
        
        int dp[len1][len2];  //当len1,len2太大的时候,会导致段错误。如果可以使用把这个数组放在堆里面的话,会好很多。
        
        memset(dp, 0, sizeof(dp));
        for(int i=1;i<len1+1;i++)
        {
            for(int j=1;j<len2+1;j++)
            {
                if(s1[i-1]==s2[j-1])
                {
                    dp[i][j] = dp[i-1][j-1]+1;
                }
                else{
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
        
        if(dp[len1][len2] == 0)
            return "-1";
        //该如何找到LCS娜?
        //寻找方法就是 如果对应的s1[i] == s2[j]的话,就说明上一个最大LCS来自于dp[i-1][j-1]
        //否则,来自于dp[i-1][j] dp[i][j-1]中比较大的那个对应的字符串
        char lcs[dp[len1][len2]]; //肯定有这么多的元素
        int cur = dp[len1][len2]-1; //指示应该插在lcs的那一个位置,因为是倒着查,因此倒着插
        
        
        while(true)
        {
            if(s1[len1-1] == s2[len2-1]) //这个地方是因为dp[i][j]对应的元素是s1[i]和s2[j]
            {
                if(cur<0)
                {
                    return string(lcs);
                }
                lcs[cur--] = s1[len1-1];
                len1--;
                len2--;
            }
            else{
                if(dp[len1-1][len2]>dp[len1][len2-1])
                {
                    len1--;
                }
                else{
                    len2--;
                }
            }
        }
    }

----------------------------end--------------------------------
如果有帮助的话,不妨点个赞再走

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值