最长公共子序列 Longest Common SubSequence

题目地址:https://leetcode.com/problems/longest-common-subsequence/

递归法

image.png
image.png

递归法存在大量重复计算,因此非常耗时。


迭代法 (动态规划)

迭代法则与递归法相反,从前往后算,这样保证每个元素都只计算一遍,减少了冗余
首先初始化一个 n * m 的数组为 0;然后对于数组的每个单元,通过比较其横向字符和纵向字符是否相等来确定当前单元的值的计算方法:

  • 如果相等,最长公共子序列又多了一个元素,所以当前单元的值等于其左上角单元的值加1
  • 如果不相等,则当前值等于其左边单元和上边单元值的较大者,即新加入的元素不会使得公共最长子序列变长,因此此时的最长公共子序列的值就等于两个字符串分别去掉当前元素的较大者。
    image.png

代码1

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int a[1001][1001]={0};
        for(int i=0; i<text1.size(); ++i)
            for(int j=0; j<text2.size(); ++j)
            {
                if(text1[i]==text2[j])
                {
                    a[i+1][j+1]=a[i][j]+1;
                }
                else
                {
                    if(a[i][j+1]>a[i+1][j])
                    {
                        a[i+1][j+1] = a[i][j+1];
                    }
                    else
                    {
                        a[i+1][j+1] = a[i+1][j];
                    }
                }
            }
        return a[text1.size()][text2.size()];
    }
};

Runtime:20 ms; Memory: 11.7MB

改进代码2

我们可以拿其中一个字符串A的每一个字符逐一去与另外一个字符串B进行比较,即到字符串A的第 i 个字符为止,与字符串B的最长公共子序列的长度;即如迭代法中的表格,只需要用一个一维数组(横向)来保存,然后往下逐一更新。
比如,第一步先在全0的基础上更新 纵向字符串的第一个字符 a 与 横向字符串的每一个字符的最大公共子序列的长度 (这里只是示意图,里面的值是错误的,直接搬移第一个解法的图
image.png

第2步:在上面第一个字符的基础上更新 纵向字符串的第2个字符串 d 与 横向字符串的每一个字符的最长公共子序列的长度
image.png

….

逐步往下,这样子需要 o(nxm) 的时间,但是只需要o(max(m,n))的空间,
这里有一个要注意的点:就是当遇到两个字符相等时,我们执行 a[j+1]=a[j]+1,这里的a[j]可能在当前层的上一步被更新,而实际上我们需要的是前一层的值,因此需要用一个临时变量把前一个的值保存下来

错误改进代码

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int a[1001]={0};
        for(int i=0; i<text1.size(); ++i)
        {
            int temp = 0;
            for(int j=0; j<text2.size(); ++j)
            {
                if(text1[i]==text2[j])
                {
                    a[j+1]=a[j]+1; // 这里错误,因为a[j]在当前层可能会被更新,而我们实际上需要的是上一层的值,因此需要一个变量将之前的存起来
                }
                else
                {
                    a[j+1]=(a[j+1]>a[j]?a[j+1]:a[j]);
                }
            }
        }
        return a[text2.size()];
    }
};

正确改进代码

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int a[1001]={0};
        for(int i=0; i<text1.size(); ++i)
        {
            int prev = 0;
            for(int j=0; j<text2.size(); ++j)
            {
                int temp=a[j+1];
                if(text1[i]==text2[j])
                {
                    a[j+1]=prev+1; // 这里是在前一层的比较基础上,这一次的更新值不能作为当前层的更
                                                                                           // 新,所以需要一个prev变量
                }
                else
                {
                    a[j+1]=(a[j+1]>a[j]?a[j+1]:a[j]);
                }
                prev = temp;
            }
        }
        return a[text2.size()];
    }
};

Runtime: 8ms Memory: 7.7MB

原文地址:https://www.cnblogs.com/qiulinzhang/p/12382015.html