718. Maximum Length of Repeated Subarray

这里写图片描述

题目的大概意思,就是求两个数组中相同的最大连续子序列的长度。


题目自然是用动归来解决,但是一开始我想错来思路,代码如下:

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 从尾到头遍历,此时就可以解决上诉问题。

扫描二维码关注公众号,回复: 37513 查看本文章
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 的范例

猜你喜欢

转载自blog.csdn.net/OneDeveloper/article/details/80032221
今日推荐