每天一道LeetCode-----计算两个序列最长的公共子序列长度

来源:互联网 发布:oracle sql loader 编辑:程序博客网 时间:2024/05/29 11:08

原题链接Maximum Length of Repeated Subarray

这里写图片描述

计算两个序列最长的相同子序列的长度


简单暴力的方法是对A中每个元素遍历一遍序列B,找到相同的位置后计算从这个位置开始有多少个元素和A是相同的,唔…大概是这个样子

class Solution {public:    int findLength(vector<int>& A, vector<int>& B) {        int maxLen = 0;        for(int i = 0; i < A.size(); ++i)        {            for(int j = 0; j < B.size(); ++j)            {                if(A[i] != B[j])                    continue;                int len = 0;                int p = i, q = j;                while(p != A.size() && q != B.size() && A[p] == B[q])                {                    ++p;                    ++q;                    ++len;                }                maxLen = std::max(maxLen, len);            }        }        return maxLen;    }};

不过超时了:(,想想也觉得没这么容易


假设序列A的长度为m,序列B的长度为n。

考虑两个序列的相同子序列

A[i, i+1, ..., i+k]
B[j, j+1, ..., j+k]

将这两个子序列分别延伸到序列末尾

A[i, i+1, ..., m-1]
B[j, j+1, ..., n-1]

那么,可以把公共子序列看成是序列A[i : m-1]和B[j : n-1]的最长公共前缀

转换成动态规划,设dp[i][j]表示A[i : m-1]和B[j : n-1]的最长公共前缀,那么

if(A[i] == B[j])    dp[i][j] = 1 + dp[i + 1][j + 1];else    dp[i][j] = 0;

如果A[i] == B[j],那么A[i : m-1]和B[j : n-1]的第一个位置已经相等,只需计算A[i+1 : m-1]和B[j+1 : n-1]的最长公共前缀

如果A[i] != B[j],那么A[i : m-1]和B[j : n-1]第一个位置就不相等,显然没有公共前缀

代码如下

class Solution {public:    int findLength(vector<int>& A, vector<int>& B) {        vector<vector<int>> dp(A.size()+1, vector<int>(B.size()+1, 0));        int maxLen = 0;        for(int i = A.size() - 1; i >= 0; --i)        {            for(int j = B.size() - 1; j >= 0; --j)            {                if(A[i] == B[j])                {                    /* 更新dp[i][j] */                    dp[i][j] = 1 + dp[i + 1][j + 1];                    maxLen = std::max(maxLen, dp[i][j]);                }            }        }        return maxLen;    }};

当然了,既然是迭代法实现的动态规划,那么就能将动态规划数组降维

观察二维动态规划数组的更新情况,整个程序只用到了dp[i][j]和dp[i+1][j+1],又因为i在外层循环,所以可以改为dp[j] = dp[j+1],不过需要改变内层循环的遍历顺序

class Solution {public:    int findLength(vector<int>& A, vector<int>& B) {        vector<int> dp(B.size() + 1, 0);        int maxLen = 0;        for(int i = A.size() - 1; i >= 0; --i)        {            /* 遍历顺序颠倒 */            for(int j = 0; j < B.size(); ++j)            {                /* 相等和不相等两种情况 */                dp[j] = (A[i] == B[j]) ? 1 + dp[j + 1] : 0;                maxLen = std::max(maxLen, dp[j]);            }        }        return maxLen;    }};

为了便于理解,可以

  • 把dp[j]看成dp[i][j],表示当前的i当前的j

  • 把dp[j+1]看成dp[i+1][j+1],表示上次循环的i和下次循环的j

假设有I1, J1, J11表示某次循环时的i,j和j+1,下一轮外层循环时i,j和j+1改为I2,J1,J11。

以下称为第一次和第二次循环

在第二次循环过程中,假设正要执行但还没有执行

dp[j] = (A[i] == B[j]) ? 1 + dp[j + 1] : 0;

写成上面符号的格式是

dp[J1] = (A[I2] == B[J1]) ? 1 + dp[J11] : 0;

因为j是从0到B.size()的,所以只有更新完dp[J1]后才会更新dp[J11],当准备更新dp[J1]时,dp[J11]还没有被更新

没有被更新的意思是dp[J11]的值在上次更新后没有被改变,上次更新时的i是I1,j是J11,所以dp[J11] == dp[I1][J11]

因为i是从A.size()到0的,所以I1 = I2 + 1,所以

dp[J11] == dp[I1][J11] == dp[I2 + 1][J11]

对于正要更新的dp[J1],此时的i是I2,所以

dp[J1] == dp[I2][J1]

所以上面的更新过程可以改为

dp[I2][J1] = (A[I2] == B[J1]) ? 1 + dp[I2 + 1][J11] : 0;

由于J11 == J1 + 1,所以

dp[I2][J1] = (A[I2] == B[J1]) ? 1 + dp[I2 + 1][J1 + 1] : 0;

形式和

dp[i][j] = (A[i] == B[j]) ? 1 + dp[i + 1][j + 1] : 0;

相同

阅读全文
0 0
原创粉丝点击