加载中...
718-最长重复子数组(Maximum Length of Repeated Subarray)
发表于:2021-12-03 | 分类: 中等
字数统计: 1.6k | 阅读时长: 7分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray

英文原文

Given two integer arrays nums1 and nums2, return the maximum length of a subarray that appears in both arrays.

 

Example 1:

Input: nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
Output: 3
Explanation: The repeated subarray with maximum length is [3,2,1].

Example 2:

Input: nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]
Output: 5

 

Constraints:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100

中文题目

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

通过代码

高赞题解

暴力法(超时)时间复杂度 $O(n^3)$

[]
const findLength = (A, B) => { const m = A.length; const n = B.length; let res = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { if (A[i] == B[j]) { // 遇到相同项 let subLen = 1; // 公共子序列长度至少为1 while (i + subLen < m && j + subLen < n && A[i + subLen] == B[j + subLen]) { //新的一项也相同 subLen++; // 公共子序列长度每次增加 1,考察新的一项 } res = Math.max(subLen, res); } } } return res; };
[]
func findLength(A []int, B []int) int { m, n := len(A), len(B) res := 0 for i := 0; i < m; i++ { for j := 0; j < n; j++ { if A[i] == B[j] { subLen := 1 for i+subLen < m && j+subLen < n && A[i+subLen] == B[j+subLen] { subLen++ } if subLen > res { res = subLen } } } } return res }

动态规划:

  • A 、B数组各抽出一个前缀数组,单看它们的末尾项,如果它们俩不一样,则公共子数组肯定不包括它们俩。

  • 如果它们俩一样,则要考虑它们俩前面的子数组「能为它们俩提供多大的公共长度」。

    • 如果它们俩的前缀数组的「末尾项」不相同,由于子数组的连续性,前缀数组不能为它们俩提供公共长度

    • 如果它们俩的前缀数组的「末尾项」相同,则可以为它们俩提供公共长度:

      • 至于提供多长的公共长度?这又取决于前缀数组的末尾项是否相同……

加上注释再讲一遍

  • A 、B数组各抽出一个前缀子数组,单看它们的末尾项,如果它们俩不一样——以它们俩为末尾项形成的公共子数组的长度为0:dp[i][j] = 0

  • 如果它们俩一样,以它们俩为末尾项的公共子数组,长度保底为1——dp[i][j]至少为 1,要考虑它们俩的前缀数组——dp[i-1][j-1]能为它们俩提供多大的公共长度

    • 如果它们俩的前缀数组的「末尾项」不相同,前缀数组提供的公共长度为 0——dp[i-1][j-1] = 0

      • 以它们俩为末尾项的公共子数组的长度——dp[i][j] = 1
    • 如果它们俩的前缀数组的「末尾项」相同

      • 前缀部分能提供的公共长度——dp[i-1][j-1],它至少为 1

      • 以它们俩为末尾项的公共子数组的长度 dp[i][j] = dp[i-1][j-1] + 1

  • 题目求:最长公共子数组的长度。不同的公共子数组的末尾项不一样。我们考察不同末尾项的公共子数组,找出最长的那个。(注意下图的最下方的一句话)

image.png

状态转移方程

  • dp[i][j] :长度为i,末尾项为A[i-1]的子数组,与长度为j,末尾项为B[j-1]的子数组,二者的最大公共后缀子数组长度。

    • 如果 A[i-1] != B[j-1], 有 dp[i][j] = 0

    • 如果 A[i-1] == B[j-1] , 有 dp[i][j] = dp[i-1][j-1] + 1

  • base case:如果i==0||j==0,则二者没有公共部分,dp[i][j]=0

  • 最长公共子数组以哪一项为末尾项都有可能,求出每个 dp[i][j],找出最大值。

image.png

代码

  • 时间复杂度 $O(n * m)$。 空间复杂度 $O(n * m)$。

  • 降维后空间复杂度 $O(n)$,如果没有空间复杂度的要求,降不降都行。

[]
const findLength = (A, B) => { const m = A.length; const n = B.length; const dp = new Array(m + 1); for (let i = 0; i <= m; i++) { // 初始化整个dp矩阵,每个值为0 dp[i] = new Array(n + 1).fill(0); } let res = 0; // i=0或j=0的base case,初始化时已经包括 for (let i = 1; i <= m; i++) { // 从1开始遍历 for (let j = 1; j <= n; j++) { // 从1开始遍历 if (A[i - 1] == B[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } // A[i-1]!=B[j-1]的情况,初始化时已包括了 res = Math.max(dp[i][j], res); } } return res; };
[]
func findLength(A []int, B []int) int { m, n := len(A), len(B) res := 0 dp := make([][]int, m+1) for i := 0; i <= m; i++ { dp[i] = make([]int, n+1) } for i := 1; i <= m; i++ { for j := 1; j <= n; j++ { if A[i-1] == B[j-1] { dp[i][j] = dp[i-1][j-1] + 1 } if dp[i][j] > res { res = dp[i][j] } } } return res }

降维优化

  • dp[i][j] 只依赖上一行上一列的对角线的值,所以我们从右上角开始计算。

  • 一维数组 dp , dp[j] 是以 A[i-1], B[j-1] 为末尾项的最长公共子数组的长度

image.png

[]
const findLength = (A, B) => { const m = A.length; const n = B.length; const dp = new Array(n + 1).fill(0); let res = 0; for (let i = 1; i <= m; i++) { for (let j = n; j >= 1; j--) { if (A[i - 1] == B[j - 1]) { dp[j] = dp[j - 1] + 1; } else { dp[j] = 0; } res = Math.max(dp[j], res); } } return res; };
[]
func findLength(A []int, B []int) int { m, n := len(A), len(B) res := 0 dp := make([]int, n+1) for i := 1; i <= m; i++ { for j := n; j >= 1; j-- { if A[i-1] == B[j-1] { dp[j] = dp[j-1] + 1 } else { dp[j] = 0 } if dp[j] > res { res = dp[j] } } } return res }

如果有帮助,留个赞鼓励我继续写下去;如果没看懂就告诉我,一定是我写得不明白,我会修改。

统计信息

通过次数 提交次数 AC比率
90451 160201 56.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
长度最小的子数组 中等
上一篇:
717-1比特与2比特字符(1-bit and 2-bit Characters)
下一篇:
719-找出第 k 小的距离对(Find K-th Smallest Pair Distance)
本文目录
本文目录