LeetCode718. 最长重复子数组

161 阅读1分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

题目

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

解题思路

动态规划

首先我们可以考虑到公共子数组在A、B两个数组里都是连续的,当递推过程获取公共子数组长度时,都是需要前一个的公共子数组长度,而且还需要知道前一个在A、B数组中的位置,再来对比接下来的数字是否一样,一样长度就加一。因此我们就想到使用dp[i][j]来记录最长公共子序列长度,这样也记录了相同数字在A,B中的位置。但是存在一个问题,对于dp[0][j]和dp[i][0]是难以在递推过程中使用dp[-1][j]和dp[i][-1]推导来,也难以初始化。我们可以退而求其次,使用dp[i][j]记录为A[i:]和B[j:]最长公共前缀长度。

  1. dp[i][j]的定义

dp[i][j]表示A[i:]和B[j:]最长公共前缀长度

  1. 状态转移方程

A[i:]和B[j:]最长公共前缀长度dp[i][j]是由dp[i-1][j-1]推导,如果nums1[i - 1] === nums2[j - 1],dp[i][j] = dp[i-1][j-1] + 1,如果nums1[i - 1] !== nums2[j - 1],dp[i][j] = 0, 这个我们可以在初始化的时候就直接设置为0。


for (let i = 1; i <= la; i++) {
        for (let j = 1; j <= lb; j++) {
            if(nums1[i - 1] === nums2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                res = Math.max(dp[i][j],res);
            }
        }
    }
  1. dp[i][j]的初始化

dp[0][j]和dp[i][0]其最长公共前缀长度一定是0, 初始化为0即可

  1. 确定遍历顺序

dp[i][j] 是dp[i-1][j-1]推导而来,那么遍历i,j一定是从前向后遍历。

因此我们可以在更新dp值的时候,对比比较,以此记录最长重复子数组长度

var findLength = function(nums1, nums2) {
    let la = nums1.length;
    let lb = nums2.length;
    let dp = Array.from(Array(la + 1),() => Array(lb + 1).fill(0));
    let res = 0;
    for (let i = 1; i <= la; i++) {
        for (let j = 1; j <= lb; j++) {
            if(nums1[i - 1] === nums2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                res = Math.max(dp[i][j],res);
            }
        }
    }
    return res;
};