搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > 数据结构和算法 > 407,动态规划和滑动窗口解决最长重复子数组

407,动态规划和滑动窗口解决最长重复子数组

数据结构和算法 2020-08-02

Never be afraid to reach for the stars, because even if you fall, you'll always be wearing a parent-chute.

永远不要害怕去摘星星,因为就算你跌下来,你永远有“父母牌”降落伞防身。

问题描述



给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。


示例:

输入:

A: [1,2,3,2,1]

B: [3,2,1,4,7]

输出:3

解释:

长度最长的公共子数组是 [3, 2, 1] 。


提示:

1 <= len(A), len(B) <= 1000

0 <= A[i], B[i] < 100


动态规划



这题一看就知道其实就是求最长公共子串问题,不懂的可以看下前面的,只不过换了种说法,换汤不换药,本质还是没变。我们就以题中的示例画个图来看一下

407,动态规划和滑动窗口解决最长重复子数组

最长的公共子数组就是上面红色所对应的[3,2,1],长度是3。

递推公式是

if(s1.charAt(i) == s2.charAr(j)) dp[i][j] = dp[i-1][j-1] + 1;else dp[i][j] = 0;

有了递推公式,代码就容易多了,我们来看下完整代码

 1public int findLength(int[] A, int[] B) {
2    int max = 0;
3    int[][] dp = new int[A.length + 1][B.length + 1];
4    for (int i = 1; i <= A.lengthi++) {
5        for (int j = 1; j <= B.length; j++) {
6            if (A[i - 1] == B[j - 1]) {
7                dp[i][j] = dp[i - 1][j - 1] + 1;
8                max = Math.max(max, dp[i][j]);
9            }
10        }
11    }
12    return max;
13}
14

这里的二维数组dp长和宽都要加1是为了减少判断,当然也可以不加1,但这样会多了一些边界的判断,我们来看下不加1的代码

 1public int findLength(int[] A, int[] B) {
2    int max = 0;
3    int[][] dp = new int[A.length][B.length];
4    for (int i = 0; i A.lengthi++) {
5        for (int j = 0; j < B.lengthj++) {
6            if (A[i] == B[j]) {
7                if (i >
 0 && j > 0)
8                    dp[i][j] = dp[i - 1][j - 1] + 1;
9                else
10                    dp[i][j] = 1;
11                max = Math.max(max, dp[i][j]);
12            }
13        }
14    }
15    return max;
16}

如果看过之前写的,我们还可以把二维数组改为一维数组来减少空间复杂度

 1public int findLength(int[] A, int[] B) {
2    int max = 0;
3    int[] dp = new int[B.length + 1];
4    for (int i = 1; i <= A.length; i++) {
5        for (int j = B.length; j >= 1; j--) {
6            if (A[i - 1] == B[j - 1])
7                dp[j] = dp[j - 1] + 1;
8            else
9                dp[j] = 0;
10            max = Math.max(max, dp[j]);
11        }
12    }
13    return max;
14}

注意这里第二个for循环是从后往前遍历的,这是因为dp后面的值会依赖前面的值,但前面的值不会依赖后面的值,如果我们改变后面的值对前面的值不会有影响,但改变前面的值会影响面的值,所以这里我们从后往前计算是最合适的。


滑动窗口