原题链接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;
相同