AlgoMooc
你已开通华为OD训练营权益,还差最后一步——完成入营激活(兑换课程 + 加飞书 + 登记服务群),即可解锁全部课程与专属服务。去激活 →
← OD 20 天课程

华为 OD 训练营 · 题解精讲

LC718. 最长重复子数组(HJ75. 公共子串计算)

配套讲解:视频讲解

LC718. 最长重复子数组(HJ75. 公共子串计算) 视频地址:https://uha.xet.tech/s/3nGx3M

题目描述

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

题目解析

解题思路

本题要求两个数组中最长公共子数组(连续子序列)的长度。参考代码使用动态规划解决。

定义 dp[i][j] 表示以 nums1[i-1]nums2[j-1] 结尾的最长公共子数组的长度。当 nums1[i-1] == nums2[j-1] 时,当前元素可以接在前一对元素之后,即 dp[i][j] = dp[i-1][j-1] + 1;否则 dp[i][j] = 0。最终答案为所有 dp[i][j] 中的最大值。

关键步骤

1. 初始化 dp 数组:大小为 (m+1) × (n+1),所有元素初始为 0。第 0 行和第 0 列表示其中一个数组为空的情况,公共子数组长度为 0。

2. 双重循环遍历i 从 1 到 mj 从 1 到 n,对应比较 nums1[i-1]nums2[j-1]

3. 状态转移:当元素相等时,dp[i][j] = dp[i-1][j-1] + 1,并更新全局最大值。

示意图(以示例 A=[1,2,3,2,1], B=[3,2,1,4,7] 为例,仅展示部分 dp 值):


      B:   3  2  1  4  7
       0  0  0  0  0  0
A: 1  0  0  0  1  0  0
   2  0  0  1  0  0  0
   3  0  1  0  0  0  0
   2  0  0  2  0  0  0
   1  0  0  0  3  0  0

i=5, j=3 时,nums1[4]=1nums2[2]=1 相等,dp[5][3] = dp[4][2] + 1 = 2 + 1 = 3,得到最大值 3。

复杂度分析

  • 时间复杂度:O(m × n),其中 m 和 n 分别为两个数组的长度。需要遍历所有 i、j 组合。
  • 空间复杂度:O(m × n),用于存储 dp 二维数组。

参考代码

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        # 获取 nums1 的长度
        m = len(nums1)
        
        # 获取 nums2 的长度
        n = len(nums2)
        
        # 设置数组 dp,用来存储 nums1 和 nums2 公共的、长度最长的子数组的长度
        # dp[0][0] 表示 nums1 前 0 个元素和 nums2 前 0 个元素的公共的、长度最长的子数组的长度
        # dp[2][3] 表示 nums1 前 2 个元素和 nums2 前 3 个元素的公共的、长度最长的子数组的长度
        # dp[i][j] 表示 nums1 前 i 个元素和 nums2 前 j 个元素的公共的、长度最长的子数组的长度
        # 前 i 个元素的区间为 [ 0 , i - 1 ]
        # dp[m][n] 表示 nums1 前 m 个元素和 nums2 前 n 个元素的公共的、长度最长的子数组的长度
        # 前 m 个元素的表示区间为 [ 0 ,m ],前 n 个元素的表示区间为 [ 0 ,n ]
        # 因此,dp 数组的长度为 m + 1 和 n + 1
        dp = [[0] * (n + 1 ) for _ in range( m + 1 )]

        # maxLength 表示 dp 数组中的最大值
        maxLength = 0
        
        # dp[0][0] 表示 nums1 前 0 个元素和 nums2 前 0 个元素的公共的、长度最长的子数组的长度
        # nums1 nums2 也没有元素
        # 因此,公共的、长度最长的子数组的长度为 0
        dp[0][0] = 0

        # nums1 没有元素 或者 nums2 没有字符时,公共的、长度最长的子数组的长度都为 0
        for i in range( 0 , m + 1 ) : 
            dp[i][0] = 0
        

        for j in range( 0 , n + 1 ) : 
            dp[0][j] = 0
        

        # i 从 1 开始,直到 m 位置,遍历 nums1 的前 i 个元素
        for i in range( 1 , m + 1 ) : 

              # j 从 1 开始,直到 n 位置,遍历 nums2 的前 j 个元素
              for j in range( 1 , n + 1 ) : 

                # 如果发现 nums1 的当前元素,即位置为 i - 1 的元素
                # 与 nums2 的当前元素,即位置为 j - 1 的元素【相同】
                # 此时,找到了一个公共元素,公共的、长度最长的子数组的长度加 1
                if  nums1[i - 1] == nums2[j - 1] : 

                    # dp[i][j] 表示 nums1 前 i 个元素和 nums2 前 j 个元素的公共的、长度最长的子数组的长度
                    # dp[i - 1][j - 1] 表示 nums1 前 i - 1 个元素和 nums2 前 j - 1 个元素的公共的、长度最长的子数组的长度
                    dp[i][j] = dp[i - 1][j - 1] + 1

                    # 更新最长的子数组长度
                    maxLength = max(maxLength, dp[i][j])

        
        # 返回结果
        return maxLength