滑动窗口算法_算法题407:动态规划和滑动窗口解决最长重复子数组

这篇博客介绍了如何使用动态规划和滑动窗口算法解决寻找两个整数数组中最长公共子数组的问题。通过示例和图表解释了这两种方法的工作原理,并提供了相应的代码实现。动态规划解决方案通过二维或一维数组实现,而滑动窗口方法通过移动第二个数组的位置找到最长子数组。两种方法在理解和代码实现上各有优势。
摘要由CSDN通过智能技术生成

(给算法爱好者加星标,修炼编程内功)

来源:山大王wld

问题描述

给两个整数数组 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

动态规划

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

adb6ced3c848e0e7fb762b48a9d46f71.png

最长的公共子数组就是上面红色所对应的[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.length; i++) { 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.length; i++) {
5        for (int j = 0; j B.length; j++) {
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后面的值会依赖前面的值,但前面的值不会依赖后面的值,如果我们改变后面的值对前面的值不会有影响,但改变前面的值会影响面的值,所以这里我们从后往前计算是最合适的。

滑动窗口

第2种方式是滑动窗口,文字叙述不好理解,我们就以[1, 2, 3, 2, 1]和[3,2,1,4]为例来举例说明,这两个数组我故意弄成两个长度不一样的,我们画个图来看一下

4cb3632a1491dc98ee787638b77c7e25.png

b08562bcb84304bbc183aa153bc64da9.png

相当于说第一个数组位置不动,第二个数组每次往右移一位,搞懂了上的分析过程,代码就容易多了,我们来看下

 1public int findLength(int[] A, int[] B) {
2    if (A.length  3        return findLengthHelper(A, B);
4    return findLengthHelper(B, A);
5}
6
7public int findLengthHelper(int[] A, int[] B) {
8    int aLength = A.length, bLength = B.length;
9    //total是总共运行的次数
10    int total = aLength + bLength - 1;
11    int max = 0;
12    for (int i = 0; i 13        //下面一大坨主要判断数组A和数组B比较的起始位置和比较的长度
14        int aStart = 0;
15        int bStart = 0;
16        int len = 0;
17        if (i 18            aStart = aLength - i - 1;
19            bStart = 0;
20            len = i + 1;
21        } else {
22            aStart = 0;
23            bStart = i - aLength + 1;
24            len = Math.min(bLength - bStart, aLength);
25        }
26        int maxlen = maxLength(A, B, aStart, bStart, len);
27        max = Math.max(max, maxlen);
28    }
29    return max;
30}
31
32//计算A和B在上面图中红色框内的最大长度
33public int maxLength(int[] A, int[] B, int aStart, int bStart, int len) {
34    int max = 0, count = 0;
35    for (int i = 0; i 36        if (A[aStart + i] == B[bStart + i]) {
37            count++;
38            max = Math.max(max, count);
39        } else {
40            count = 0;
41        }
42    }
43    return max;
44}

总结

其实这道题求的就是最长公共子串问题,通过上面的图分析,可以发现第一种方式和第二种方式都比较好理解,但第一种方式代码明显比第二种少了很多。

- EOF -

推荐阅读   点击标题可跳转

1、经典动态规划:戳气球问题

2、一文学会动态规划解题技巧

3、这道算法题用「动态规划」求解可麻烦了!

觉得本文有帮助?请分享给更多人

关注「算法爱好者」加星标,修炼编程内功

f8ef2866fa95f44d73fa04ebae8df804.png

好文章,我在看❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值