最长重复子数组的变种:最多允许k次修改的最长公共子数组
字数 1308 2025-12-03 01:25:21

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

题目描述
给定两个整数数组nums1和nums2,以及一个整数k。要求找到最长的公共子数组(连续),且允许在任意一个数组中最多次修改k个元素的值(修改后元素可以变成任意值)。返回最长公共子数组的长度。

示例:
输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7], k = 1
输出:3
解释:最长公共子数组是[3,2,1],在nums2中只需修改1个元素(将4改为1)即可匹配。

解题过程

1. 问题分析
这是最长公共子数组问题(最长公共连续子序列)的变种,增加了"最多允许k次修改"的条件。我们需要找到两个数组中最长的连续公共部分,但允许最多k个位置不匹配(通过修改来匹配)。

关键点:

  • 子数组必须是连续的
  • 允许最多k次修改(在两个数组中的任意位置)
  • 修改可以将元素改为任意值

2. 基本思路
使用动态规划,但需要记录匹配状态和已使用的修改次数。定义dp[i][j][m]表示以nums1的第i个元素和nums2的第j个元素结尾,使用了m次修改的最长公共子数组长度。

3. 状态转移方程

对于每个位置(i, j)和修改次数m:

  • 如果nums1[i] == nums2[j]:
    • 不需要使用修改,继承前一个状态
    • dp[i][j][m] = dp[i-1][j-1][m] + 1
  • 如果nums1[i] != nums2[j]:
    • 如果还有修改次数可用(m > 0):
      • 使用一次修改来匹配
      • dp[i][j][m] = dp[i-1][j-1][m-1] + 1
    • 如果没有修改次数可用:
      • dp[i][j][m] = 0(无法匹配)

边界条件:

  • 当i=0或j=0时,dp[i][j][m] = 1(如果nums1[i] == nums2[j])或0(如果不相等且m=0)

4. 优化空间复杂度
由于dp[i][j][m]只依赖于dp[i-1][j-1][m]和dp[i-1][j-1][m-1],我们可以使用滚动数组优化,将三维dp降为二维。

定义dp[j][m]表示当前行以nums2的第j个元素结尾,使用了m次修改的最长公共子数组长度。

5. 具体实现步骤

def findLength(nums1, nums2, k):
    m, n = len(nums1), len(nums2)
    # 初始化dp数组
    dp = [[0] * (k + 1) for _ in range(n + 1)]
    max_len = 0
    
    for i in range(1, m + 1):
        # 保存上一行的状态
        prev = [row[:] for row in dp]
        
        for j in range(1, n + 1):
            for mod in range(k + 1):
                if nums1[i-1] == nums2[j-1]:
                    # 元素相等,直接继承前一个状态
                    dp[j][mod] = prev[j-1][mod] + 1
                elif mod > 0:
                    # 元素不等但还有修改次数
                    dp[j][mod] = prev[j-1][mod-1] + 1
                else:
                    # 元素不等且没有修改次数
                    dp[j][mod] = 0
                
                # 更新最大值
                max_len = max(max_len, dp[j][mod])
    
    return max_len

6. 时间复杂度分析

  • 时间复杂度:O(m × n × k),其中m和n分别是数组长度,k是允许的修改次数
  • 空间复杂度:O(n × k),通过滚动数组优化

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

计算过程:

  • 当i=3, j=1时(对应元素3和3):匹配,长度=1
  • 当i=4, j=2时(对应元素2和2):匹配,长度=2
  • 当i=5, j=3时(对应元素1和1):匹配,长度=3
  • 最终找到最长公共子数组长度为3

8. 进一步优化思路
对于更大的k值,可以考虑使用滑动窗口+二分查找的方法,将时间复杂度优化到O((m+n)×log(min(m,n))),但这会牺牲一些准确性。

最长重复子数组的变种:最多允许k次修改的最长公共子数组 题目描述 给定两个整数数组nums1和nums2,以及一个整数k。要求找到最长的公共子数组(连续),且允许在任意一个数组中最多次修改k个元素的值(修改后元素可以变成任意值)。返回最长公共子数组的长度。 示例: 输入:nums1 = [ 1,2,3,2,1], nums2 = [ 3,2,1,4,7 ], k = 1 输出:3 解释:最长公共子数组是[ 3,2,1 ],在nums2中只需修改1个元素(将4改为1)即可匹配。 解题过程 1. 问题分析 这是最长公共子数组问题(最长公共连续子序列)的变种,增加了"最多允许k次修改"的条件。我们需要找到两个数组中最长的连续公共部分,但允许最多k个位置不匹配(通过修改来匹配)。 关键点: 子数组必须是连续的 允许最多k次修改(在两个数组中的任意位置) 修改可以将元素改为任意值 2. 基本思路 使用动态规划,但需要记录匹配状态和已使用的修改次数。定义dp[ i][ j][ m ]表示以nums1的第i个元素和nums2的第j个元素结尾,使用了m次修改的最长公共子数组长度。 3. 状态转移方程 对于每个位置(i, j)和修改次数m: 如果nums1[ i] == nums2[ j ]: 不需要使用修改,继承前一个状态 dp[ i][ j][ m] = dp[ i-1][ j-1][ m ] + 1 如果nums1[ i] != nums2[ j ]: 如果还有修改次数可用(m > 0): 使用一次修改来匹配 dp[ i][ j][ m] = dp[ i-1][ j-1][ m-1 ] + 1 如果没有修改次数可用: dp[ i][ j][ m ] = 0(无法匹配) 边界条件: 当i=0或j=0时,dp[ i][ j][ m] = 1(如果nums1[ i] == nums2[ j ])或0(如果不相等且m=0) 4. 优化空间复杂度 由于dp[ i][ j][ m]只依赖于dp[ i-1][ j-1][ m]和dp[ i-1][ j-1][ m-1 ],我们可以使用滚动数组优化,将三维dp降为二维。 定义dp[ j][ m ]表示当前行以nums2的第j个元素结尾,使用了m次修改的最长公共子数组长度。 5. 具体实现步骤 6. 时间复杂度分析 时间复杂度:O(m × n × k),其中m和n分别是数组长度,k是允许的修改次数 空间复杂度:O(n × k),通过滚动数组优化 7. 示例验证 以nums1 = [ 1,2,3,2,1], nums2 = [ 3,2,1,4,7 ], k = 1为例: 计算过程: 当i=3, j=1时(对应元素3和3):匹配,长度=1 当i=4, j=2时(对应元素2和2):匹配,长度=2 当i=5, j=3时(对应元素1和1):匹配,长度=3 最终找到最长公共子数组长度为3 8. 进一步优化思路 对于更大的k值,可以考虑使用滑动窗口+二分查找的方法,将时间复杂度优化到O((m+n)×log(min(m,n))),但这会牺牲一些准确性。