最长重复子数组的变种:最多允许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
这个问题的关键在于理解"修改"操作的含义,以及如何在不相等的情况下继续扩展子数组的长度。