最长连续递增序列的变种:最多允许删除k个元素的最长连续递增子序列
字数 858 2025-12-01 18:31:18
最长连续递增序列的变种:最多允许删除k个元素的最长连续递增子序列
题目描述
给定一个整数数组nums和一个整数k,找出最长连续递增子序列的长度,但允许最多删除k个元素。也就是说,我们可以删除最多k个元素来获得一个连续递增的子序列。
例如:
nums = [1, 3, 2, 4, 5, 2, 1], k = 1
输出:4
解释:删除元素2后得到[1, 3, 4, 5, 2, 1],其中[1, 3, 4, 5]是连续递增的,长度为4。
解题过程
步骤1:理解问题本质
我们需要找到一个最长的连续递增子序列,但允许最多跳过(删除)k个元素。这可以理解为在数组中找到一个窗口,窗口内最多有k个"破坏递增顺序"的元素。
步骤2:定义状态
我们使用滑动窗口+动态规划的思想:
- 定义dp[i]表示以第i个元素结尾的最长连续递增子序列长度
- 但这里我们更需要维护一个滑动窗口,使用双指针技巧
步骤3:滑动窗口解法
- 初始化left = 0, max_len = 1
- 对于每个right从1到n-1:
- 如果nums[right] > nums[right-1],继续扩展窗口
- 如果nums[right] <= nums[right-1],检查是否还能删除元素:
- 如果已删除元素数 < k,可以继续
- 否则需要移动左指针
步骤4:详细算法实现
def findLength(nums, k):
n = len(nums)
left = 0
max_len = 1
# 记录需要删除的元素数量
delete_count = 0
for right in range(1, n):
# 如果当前元素大于前一个元素,直接扩展窗口
if nums[right] > nums[right-1]:
max_len = max(max_len, right - left + 1)
continue
# 当前元素破坏递增性,需要处理
if delete_count < k:
# 还可以删除元素
delete_count += 1
max_len = max(max_len, right - left + 1)
else:
# 无法再删除,移动左指针
# 找到第一个破坏递增性的位置
while left < right and delete_count >= k:
# 如果左指针移动时跳过一个破坏点,减少delete_count
if left < n-1 and nums[left] >= nums[left+1]:
delete_count = max(0, delete_count - 1)
left += 1
# 处理当前元素
if nums[right] <= nums[right-1]:
delete_count += 1
max_len = max(max_len, right - left + 1)
return max_len
步骤5:优化解法
上面的解法可能不够完善,我们使用更清晰的动态规划思路:
定义dp[i][j]:以i结尾,使用了j次删除操作的最长序列长度
状态转移:
- 不删除当前元素:
if nums[i] > nums[i-1]:
dp[i][j] = dp[i-1][j] + 1
else:需要删除nums[i-1]或者跳过
if j > 0:
dp[i][j] = dp[i-1][j-1] + 1
步骤6:最终优化解法
def findLength(nums, k):
n = len(nums)
if n == 0:
return 0
# dp[i][j]: 以i结尾,使用了j次删除的最长长度
dp = [[1] * (k+1) for _ in range(n)]
max_len = 1
for i in range(1, n):
for j in range(k+1):
# 情况1:nums[i]直接接在nums[i-1]后面
if nums[i] > nums[i-1]:
dp[i][j] = dp[i-1][j] + 1
# 情况2:使用一次删除机会跳过破坏递增的元素
elif j > 0:
dp[i][j] = dp[i-1][j-1] + 1
# 情况3:尝试连接更早的元素
for p in range(i-2, max(-1, i-j-2), -1):
if p >= 0 and nums[i] > nums[p] and j - (i-p-1) >= 0:
remaining_deletes = j - (i-p-1)
dp[i][j] = max(dp[i][j], dp[p][remaining_deletes] + 1)
max_len = max(max_len, dp[i][j])
return max_len
步骤7:最优解法(滑动窗口)
def findLength(nums, k):
n = len(nums)
left = 0
max_len = 1
# 记录窗口中破坏递增性的位置
violations = []
for right in range(1, n):
if nums[right] <= nums[right-1]:
violations.append(right)
# 如果破坏点太多,移动左指针
while len(violations) > k:
# 移动左指针到第一个破坏点之后
left = violations.pop(0) + 1
# 重新计算破坏点
temp = []
for i in range(left, right):
if nums[i] <= nums[i-1]:
temp.append(i)
violations = temp
max_len = max(max_len, right - left + 1)
return max_len
这个解法的时间复杂度是O(n),空间复杂度是O(k),是最优的解决方案。