代码随想录 718.最长重复子数组

思路:题目中的子数组就是指连续子序列。

(1)暴力:要求两个数组中的最长重复子数组,就是两层for循环确定两个数组的起始位置,然后再来一个for循环可以是for或while,从两个起始位置开始比较,取得重复子数组的长度。

(2)动规:用二维数组记录两个字符串的所有比较情况,便于推导递推公式。

动规五部曲如下所示:

1.确定dp数组(dp table)及其下标的含义:

(1)dp[i][j]表示以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组的长度为dp[i][j](以下标i - 1为结尾的A,一定是以A[i - 1]为结尾的字符串)。

(2)dp[0][0]没有含义,本题遍历dp[i][j]时i和j都要从1开始。

2.确定递推公式:

(1)根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来,即当A[i - 1]和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1。

(2)根据递推公式可以看出,遍历i和j都要从1开始。

3.dp数组如何初始化:

(1)根据dp[i][j]的定义,dp[i][0]和dp[0][j]都是没有意义的,但dp[i][0]和dp[0][j]要赋初始值,为了方便递推公式dp[i][j] = dp[i - 1][j - 1] + 1。所以dp[i][0]和dp[0][j]初始化为0。

(2)举个例子,如果A[0] = B[0],那么dp[1][1] = dp[0][0] + 1,只有在dp[0][0]初始化为0时,正好符合递推公式逐步累加起来。

4.确定遍历顺序:外层for循环遍历A,内层for循环遍历B(反过来也行)。同时题目要求求长度最长的子数组的长度,所以在遍历时顺便把dp[i][j]的最大值记录下来。代码如下所示。

cpp 复制代码
for (int i = 1; i <= nums1.size(); i++) {
    for (int j = 1; j <= nums2.size(); j++) {
        if (nums1[i - 1] == nums2[j - 1]) {
            dp[i][j] = dp[i - 1][j - 1] + 1;
        }
        if (dp[i][j] > result) result = dp[i][j];
    }
}

5.举例推导dp数组:以示例1中,A[1,2,3,2,1],B[3,2,1,4,7]为例,画一个dp数组的状态变化如下图所示。

附代码:

(一)dp[i][j]表示以下标i - 1结尾的A,和以下标j - 1结尾的B,最长重复子数组的长度为dp[i][j]。

java 复制代码
//dp[i][j]表示以下标i - 1结尾的A,和以下标j - 1为结尾的B,最长重复子数组的长度为dp[i][j]
class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int res = 0;
        int[][] dp = new int[nums1.length + 1][nums2.length + 1];
        for(int i = 1;i < nums1.length + 1;i++){
            for(int j = 1;j < nums2.length + 1;j++){
                if(nums1[i - 1] == nums2[j - 1]){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    res = Math.max(res,dp[i][j]);
                }
            }
        }
        return res;
    }
}

(二)dp[i][j]表示以下标i结尾的A,和以下标j结尾的B,最长重复子数组的长度为dp[i][j]。

java 复制代码
class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int res = 0;
        int[][] dp = new int[nums1.length + 1][nums2.length + 1];
        for(int i = 0;i < nums1.length;i++){
            if(nums1[i] == nums2[0]){
                dp[i][0] = 1;
            }
        }
        for(int j = 0;j < nums2.length;j++){
            if(nums2[j] == nums1[0]){
                dp[0][j] = 1;
            }
        }
        for(int i = 0;i < nums1.length;i++){
            for(int j = 0;j < nums2.length;j++){
                if(nums1[i] == nums2[j] && i > 0 && j > 0){//防止i - 1出现负数
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                if(dp[i][j] > res){
                    res = dp[i][j];
                }
            }
        }
        return res;
    }
}

(三)滚动数组,一维dp。

思路:由于dp[i][j]都是由dp[i - 1][j - 1]推出的,那么压缩为一维数组,dp[j]都是由dp[j - 1]推出的。也就是相当于把上一层的dp[i - 1][j]拷贝到下一层的dp[i][j]来继续用。此时遍历数组B的时候,就是从后向前遍历,以避免重复覆盖

附代码:

java 复制代码
class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int[] dp = new int[nums2.length + 1];
        int res = 0;
        for(int i = 1;i <= nums1.length;i++){
            for(int j = nums2.length;j > 0;j--){
                if(nums1[i - 1] == nums2[j - 1]){
                    dp[j] = dp[j - 1] + 1;
                }else{
                    dp[j] = 0;
                }
                res = Math.max(res,dp[j]);
            }
        }
        return res;
    }
}
相关推荐
谎言西西里13 小时前
LeetCode 热题100 --- 双指针专区
算法
leo__52016 小时前
基于两步成像算法的聚束模式SAR MATLAB实现
开发语言·算法·matlab
前端小白在前进17 小时前
力扣刷题:在排序数组中查找元素的第一个和最后一个位置
数据结构·算法·leetcode
某林21218 小时前
基于SLAM Toolbox的移动机器人激光建图算法原理与工程实现
stm32·嵌入式硬件·算法·slam
修炼地18 小时前
代码随想录算法训练营第四十三天 | 图论理论基础、深搜理论基础、卡码网98. 所有可达路径、797. 所有可能的路径、广搜理论基础
算法·深度优先·图论
iAkuya18 小时前
(leetcode)力扣100 23反转链表(迭代||递归)
算法·leetcode·链表
剪一朵云爱着18 小时前
PAT 1095 Cars on Campus
算法·pat考试
MicroTech202519 小时前
激光点云快速配准算法创新突破,MLGO微算法科技发布革命性点云配准算法技术
人工智能·科技·算法
Cathy Bryant20 小时前
傅里叶变换(一):简介
笔记·算法·数学建模·信息与通信·傅里叶分析
allan bull20 小时前
在节日中寻找平衡:圣诞的欢乐与传统节日的温情
人工智能·学习·算法·职场和发展·生活·求职招聘·节日