题目的大概意思,就是求两个数组中相同的最大连续子序列的长度。
题目自然是用动归来解决,但是一开始我想错来思路,代码如下:
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 的范例