
题目的大概意思,就是求两个数组中相同的最大连续子序列的长度。
题目自然是用动归来解决,但是一开始我想错来思路,代码如下:
public int findLength2(int[] A, int[] B) {
int[][] dp = new int[A.length][B.length];
dp[0][0] = A[0] == B[0] ? 1 : 0;
for (int j = 1; j < B.length; j++) {
dp[0][j] = A[0] == B[j] ? 1 : dp[0][j - 1];
}
for (int i = 1; i < A.length; i++) {
dp[i][0] = A[i] == B[0] ? 1 : dp[i - 1][0];
}
for (int i = 1; i < A.length; i++) {
for (int j = 1; j < B.length; j++) {
if (A[i]==B[j]) {
if (A[i-1]==B[j-1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
dp[i][j] = Math.max(dp[i][j], 1);
}
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
return dp[A.length - 1][B.length - 1];
}
此时,我的思路就是用辅助数组 dp,其中 dp[i][j] 代表 A[0,i] 和 B[0,j] 两个序列中,存在的相同的连续子序列中最大长度值。因此就有如下的转移方程:
当 A[i]==B[j] 时,就看 A[i-1]==B[j-1] 是否 成立,
如果相等就在 dp[i-1][j-1] 的基础上 +1,即 dp[i][j] = dp[i - 1][j - 1] + 1
如果不想等,dp[i][j] 为 dp[i][j - 1], dp[i - 1][j],1 三者中的最大值
因此前面已经说了 dp[i][j] 代表的是 A[0,i] 和 B[0,j] 两个序列中,存在的相同的连续子序列中最大长度值,且 dp[i][j - 1], dp[i - 1][j] 都有可能为 0,而 1 的含义就是说在 A[0,i] 和 B[0,j] 中只有 A[i] 与 B[j] 两个子序列相等。
而当 A[i]!=B[j] 时,就直接取 dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j])
而问题就出在 dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]),该逻辑就导致 dp[i][j] 始终为 A[0,i] 和 B[0,j] 两个序列中存在的相同的连续子序列中最大长度值,这就会使得某些数据出现错误,如下:

显而易见,dp[i][j] 代表的含义出现了错误,不应该为 A[0,i] 和 B[0,j] 两个序列中,存在的相同的连续子序列中最大长度值,而应该是 A[0,i] 和 B[0,j] 两个序列此时以 A[i] 和 B[j] 为基点,向前比较,能够得到的最大的相同子序列的长度,则有:
if (A[i]==B[j]) {
if (A[i-1]==B[j-1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = 1;
}
} else {
dp[i][j] = 0;
}
此时,就还需要一个临时变量,用来保存 A[0,i] 和 B[0,j] 中存在的相同的连续子序列中最大长度值,这样,对于 dp[i-1][j-1] 的值就不会错误的影响到 dp[i][j] 的值。
public int findLength(int[] A, int[] B) {
int[][] dp = new int[A.length][B.length];
int maxLen = 0;
dp[0][0] = A[0] == B[0] ? 1 : 0;
for (int j = 1; j < B.length; j++) {
dp[0][j] = A[0] == B[j] ? 1 : 0;
if (maxLen<dp[0][j]) maxLen = dp[0][j];
}
for (int i = 1; i < A.length; i++) {
dp[i][0] = A[i] == B[0] ? 1 : 0;
if (maxLen<dp[i][0]) maxLen = dp[i][0];
}
for (int i = 1; i < A.length; i++) {
for (int j = 1; j < B.length; j++) {
if (A[i]==B[j]) {
if (A[i-1]==B[j-1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = 1;
}
} else {
dp[i][j] = 0;
}
if (maxLen<dp[i][j]) maxLen = dp[i][j];
}
}
return maxLen;
}
之后,还可以考虑到优化,将辅助数组从二维降为一维。
在二维的时候,对于 A 和 B 都是从头开始遍历,且 dp[i][j] 又会 dp[i - 1][j - 1],因此使用一维数组时,dp[k] 需要依赖于未更新的 dp[k-1] ,但是当到 k 时 dp[k-1] 已经被更新,所以需要换一种角度,因此可以将 A 从尾到头遍历,此时就可以解决上诉问题。
public int findLength(int[] A, int[] B) {
int m=A.length,n=B.length;
int max=0;
int[] dp=new int[n+1];
for(int i=m-1;i>=0;i--){//从后遍历数组 A
for(int j=0;j<n;j++){//从头遍历数组 B
dp[j]=(A[i]==B[j]?1+dp[j+1]:0);
max=Math.max(max,dp[j]);
}
}
return max;
}
参考自执行用时为 35 ms 的范例
最长公共子序列问题

本文探讨了求解两个数组中最长公共连续子序列的问题,并通过动态规划方法给出了两种不同的实现方案,包括使用二维数组和一维数组进行优化。
210

被折叠的 条评论
为什么被折叠?



