[LeetCode]718. 最长重复子数组

本文探讨了寻找两个整数数组中最长公共子数组的问题,提供了两种解决方案:动态规划和滑动窗口。动态规划通过构建二维数组记录公共子数组长度,而滑动窗口则通过调整数组对齐方式遍历计算。文章详细解析了两种方法的实现过程和复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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

示例:

输入:
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

解题思路

解法一:动态规划

dp[i+1][j+1] 表示以 A[i]、B[j] 结尾长度分别为 i+1、j+1 的序列的公共子数组长度,若 A[i] == B[j],则 dp[i+1][j+1] = dp[i][j] + 1;
若 A[i] !=B[j],则 dp[i+1][j+1] = 0。
初始化 dp[0][0] = 0,表示长度为0的序列的公共子数组长度为0。

复杂度分析:
时间复杂度:O(N×M)。
空间复杂度:O(N×M)。

解法二:滑动窗口

如果知道了重复子数组在两个数组中的开始位置,我们就可以根据它们将 A 和 B 进行「对齐」,然后对这两个数组进行一次遍历,得到子数组的长度,最后取全局最大值即可。
我们可以枚举 A 和 B 所有的对齐方式。对齐的方式有两类:第一类为 A 不变,B 的首元素与 A 中的某个元素对齐;第二类为 B 不变,A 的首元素与 B 中的某个元素对齐。对于每一种对齐方式,我们计算它们相对位置相同的重复子数组即可。

复杂度分析:
时间复杂度:O((M+N)×min(M,N))。
空间复杂度:O(1)。

代码

解法一:动态规划

class Solution {
    public int findLength(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        int[][] dp = new int[m+1][n+1]; // 初始化全为0
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                if(A[i] == B[j]){
                    dp[i+1][j+1] = dp[i][j] + 1;
                }else{
                    dp[i+1][j+1] = 0; // 这步不写也可以
                }
            }
        }
        int res = 0;
        for(int i=0; i<=m; i++){
            for(int j=0; j<=n; j++){
                res = Math.max(res, dp[i][j]);
            }
        }
        return res;
    }
}

解法二:滑动窗口

class Solution {
    public int findLength(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        int res = 0;
        // 保持 A 不动,B 的首元素与 A 中的某个元素对齐
        for(int i=0; i<m; i++){
            // 可以滑动的距离
            int len = Math.min(m-i, n);
            int maxLen = maxLength(A, B, i, 0, len);
            res = Math.max(maxLen, res);
        }
        // 保持 B 不动,A 的首元素与 B 中的某个元素对齐
        for(int i=0; i<n; i++){
            int len = Math.min(m, n-i);
            int maxLen = maxLength(A, B, 0, i, len);
            res = Math.max(maxLen, res);
        }
        return res;
    }

    private int maxLength(int[] A, int[] B, int startA, int startB, int len){
        int res = 0;
        int cnt = 0;
        for(int i=0; i<len; i++){
            if(A[startA+i] == B[startB+i]){
                cnt++;
            }else{
                cnt = 0;
            }
            res = Math.max(res, cnt);
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值