最长重复子数组的变种:最多允许k次元素修改的最长公共子数组
字数 1135 2025-11-25 06:10:53

最长重复子数组的变种:最多允许k次元素修改的最长公共子数组

题目描述
给定两个整数数组nums1和nums2,以及一个整数k。我们需要找到最长的公共子数组的长度,其中允许最多修改k个元素。也就是说,我们可以将nums1或nums2中的最多k个元素修改为任意值,使得这两个数组在相应位置上有更长的连续相同子数组。

解题过程

让我们循序渐进地分析这个问题:

1. 问题理解

  • 我们有两个数组nums1和nums2
  • 需要找到最长的连续子数组,使得在最多修改k个元素的情况下,这两个子数组完全相同
  • 修改操作:可以将任意位置的元素改为任意值
  • 目标:最大化满足条件的连续子数组长度

2. 基础思路
这个问题可以看作是经典的最长重复子数组问题的扩展。在经典问题中,我们要求子数组完全相等,而这里允许最多k个位置不相等。

我们可以使用动态规划来解决,但需要记录不相等元素的数量。

3. 动态规划状态定义
定义dp[i][j][m]表示以nums1的第i个元素和nums2的第j个元素结尾的子数组,在最多允许m次修改的情况下,最长的公共子数组长度。

但是这种三维DP的时间复杂度太高。我们可以优化为二维DP加上滑动窗口的方法。

4. 优化方法:滑动窗口 + 前缀和思想

更高效的方法是对于每个可能的起始位置对(i, j),计算从该位置开始的最长公共子数组,其中不相等元素的数量不超过k。

具体步骤:

步骤1:预处理差值数组
对于每个位置(i, j),我们可以计算nums1[i]和nums2[j]是否相等:

  • 如果相等,差值为0
  • 如果不相等,差值为1

步骤2:滑动窗口求解
对于每个可能的对齐方式(即nums1和nums2的相对偏移),使用滑动窗口来找到最长的子数组,其中不相等元素的数量不超过k。

具体来说,对于每个偏移量d(-len2 ≤ d ≤ len1),我们考虑:

  • nums1从max(0, -d)开始
  • nums2从max(0, d)开始
    的对应元素

步骤3:实现细节

def findLength(nums1, nums2, k):
    len1, len2 = len(nums1), len(nums2)
    result = 0
    
    # 遍历所有可能的偏移量
    for d in range(-len2 + 1, len1):
        left = 0
        right = 0
        modifications = 0
        
        # 确定当前偏移下的起始位置
        i = max(0, d)
        j = max(0, -d)
        
        while i + right < len1 and j + right < len2:
            # 检查当前元素是否相等
            if nums1[i + right] != nums2[j + right]:
                modifications += 1
            
            # 如果修改次数超过k,移动左指针
            while modifications > k:
                if nums1[i + left] != nums2[j + left]:
                    modifications -= 1
                left += 1
            
            # 更新结果
            result = max(result, right - left + 1)
            right += 1
    
    return result

5. 动态规划解法(更直观但稍慢)

我们也可以使用二维动态规划,但需要记录修改次数:

def findLength(nums1, nums2, k):
    len1, len2 = len(nums1), len(nums2)
    # dp[i][j]表示以nums1[i]和nums2[j]结尾的子数组长度
    # 同时我们需要记录达到这个长度时使用的修改次数
    dp = [[(0, 0) for _ in range(len2 + 1)] for _ in range(len1 + 1)]
    result = 0
    
    for i in range(1, len1 + 1):
        for j in range(1, len2 + 1):
            if nums1[i-1] == nums2[j-1]:
                # 元素相等,继承前一个状态
                length, mods = dp[i-1][j-1]
                dp[i][j] = (length + 1, mods)
            else:
                # 元素不相等,可以使用一次修改
                length, mods = dp[i-1][j-1]
                if mods < k:
                    dp[i][j] = (length + 1, mods + 1)
                else:
                    dp[i][j] = (0, 0)
            
            result = max(result, dp[i][j][0])
    
    return result

6. 复杂度分析

  • 滑动窗口方法:时间复杂度O((m+n)×min(m,n)),空间复杂度O(1)
  • 动态规划方法:时间复杂度O(m×n),空间复杂度O(m×n)

其中m和n分别是两个数组的长度。

7. 示例验证
例子:
nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7], k = 1

结果:最长公共子数组为[3,2,1]或[2,1,4,7](修改1个元素),长度为4

这个问题的关键在于理解"修改"操作的含义,以及如何在不相等的情况下继续扩展子数组的长度。

最长重复子数组的变种:最多允许k次元素修改的最长公共子数组 题目描述 给定两个整数数组nums1和nums2,以及一个整数k。我们需要找到最长的公共子数组的长度,其中允许最多修改k个元素。也就是说,我们可以将nums1或nums2中的最多k个元素修改为任意值,使得这两个数组在相应位置上有更长的连续相同子数组。 解题过程 让我们循序渐进地分析这个问题: 1. 问题理解 我们有两个数组nums1和nums2 需要找到最长的连续子数组,使得在最多修改k个元素的情况下,这两个子数组完全相同 修改操作:可以将任意位置的元素改为任意值 目标:最大化满足条件的连续子数组长度 2. 基础思路 这个问题可以看作是经典的最长重复子数组问题的扩展。在经典问题中,我们要求子数组完全相等,而这里允许最多k个位置不相等。 我们可以使用动态规划来解决,但需要记录不相等元素的数量。 3. 动态规划状态定义 定义dp[ i][ j][ m ]表示以nums1的第i个元素和nums2的第j个元素结尾的子数组,在最多允许m次修改的情况下,最长的公共子数组长度。 但是这种三维DP的时间复杂度太高。我们可以优化为二维DP加上滑动窗口的方法。 4. 优化方法:滑动窗口 + 前缀和思想 更高效的方法是对于每个可能的起始位置对(i, j),计算从该位置开始的最长公共子数组,其中不相等元素的数量不超过k。 具体步骤: 步骤1:预处理差值数组 对于每个位置(i, j),我们可以计算nums1[ i]和nums2[ j ]是否相等: 如果相等,差值为0 如果不相等,差值为1 步骤2:滑动窗口求解 对于每个可能的对齐方式(即nums1和nums2的相对偏移),使用滑动窗口来找到最长的子数组,其中不相等元素的数量不超过k。 具体来说,对于每个偏移量d(-len2 ≤ d ≤ len1),我们考虑: nums1从max(0, -d)开始 nums2从max(0, d)开始 的对应元素 步骤3:实现细节 5. 动态规划解法(更直观但稍慢) 我们也可以使用二维动态规划,但需要记录修改次数: 6. 复杂度分析 滑动窗口方法:时间复杂度O((m+n)×min(m,n)),空间复杂度O(1) 动态规划方法:时间复杂度O(m×n),空间复杂度O(m×n) 其中m和n分别是两个数组的长度。 7. 示例验证 例子: nums1 = [ 1,2,3,2,1], nums2 = [ 3,2,1,4,7 ], k = 1 结果:最长公共子数组为[ 3,2,1]或[ 2,1,4,7 ](修改1个元素),长度为4 这个问题的关键在于理解"修改"操作的含义,以及如何在不相等的情况下继续扩展子数组的长度。