线性动态规划:最长和谐子序列的变种:最多允许k次不和谐的元素
字数 2801 2025-12-04 08:52:27
线性动态规划:最长和谐子序列的变种:最多允许k次不和谐的元素
题目描述
给定一个整数数组 nums 和一个非负整数 k,定义和谐子序列为任意两个相邻元素的绝对差不超过 1 的子序列。但允许最多出现 k 次“不和谐”的相邻元素对(即绝对差大于 1 的相邻对)。求满足条件的最长子序列的长度。
示例
输入:nums = [1, 3, 2, 2, 5, 6, 4, 4], k = 1
输出:5
解释:子序列 [2, 2, 5, 6, 4] 中,(2,5) 的差为 3(不和谐),但仅出现 1 次(≤k=1),其余相邻对均和谐。
解题思路
-
问题分析
- 和谐子序列的常规定义要求所有相邻元素差 ≤1,但本题允许最多
k次例外。 - 若
k=0,则退化为标准和谐子序列问题(可通过哈希表计数解决)。 - 对于
k>0,需记录当前子序列中已出现的不和谐次数,并利用动态规划状态转移。
- 和谐子序列的常规定义要求所有相邻元素差 ≤1,但本题允许最多
-
状态定义
设dp[i][j]表示以nums[i]结尾、且已累积j次不和谐对的最长和谐子序列长度。- 初始状态:每个元素自身构成子序列,长度为 1,不和谐次数为 0,即
dp[i][0] = 1。 - 目标:所有
dp[i][j](j ≤ k)的最大值。
- 初始状态:每个元素自身构成子序列,长度为 1,不和谐次数为 0,即
-
状态转移
对于每个i(当前元素),枚举其前面的元素p(0 ≤ p < i):- 计算
diff = |nums[i] - nums[p]|。 - 若
diff ≤ 1,则从p到i是和谐的,不和谐次数不变:
dp[i][j] = max(dp[i][j], dp[p][j] + 1)。 - 若
diff > 1且j > 0,则从p到i不和谐,不和谐次数增加 1:
dp[i][j] = max(dp[i][j], dp[p][j-1] + 1)。 - 注意:若
j=0时出现不和谐对,则不能从p转移(因为不允许不和谐)。
- 计算
-
复杂度优化
- 直接实现需 O(n²k) 时间,可能超时。
- 优化:观察发现,和谐子序列中元素值需接近(差 ≤1),因此可将元素分组(按值排序后相邻值差 ≤1 的分为一组)。但允许不和谐对时,需谨慎处理跨组转移。
- 更优方案:先对数组排序,则和谐子序列在排序后必为连续段(允许最多 k 次“跳跃”到非相邻值)。此时问题转化为:在排序数组上找最长子序列,使得最多 k 对相邻元素差 >1。
- 排序后,用双指针维护滑动窗口,窗口内最多 k 个不和谐对,窗口长度即为候选解。
详细步骤(优化后解法)
-
排序数组
sorted_nums = sorted(nums)
示例排序后:[1, 2, 2, 3, 4, 4, 5, 6]。 -
双指针滑动窗口
- 指针
left和right表示当前窗口左右边界。 - 统计窗口内不和谐对的数量
mismatch(即满足sorted_nums[r] - sorted_nums[r-1] > 1的相邻对数)。 - 当
mismatch > k时,移动left指针缩小窗口,直到mismatch ≤ k。 - 窗口长度
(right - left + 1)即为当前和谐子序列长度(排序后子序列自动满足相邻差 ≤1 或标记为不和谐)。
- 指针
-
维护不和谐对数
- 窗口扩展(
right右移)时,检查新加入的right与right-1的差:若差 >1,则mismatch++。 - 窗口收缩(
left右移)时,检查离开的left与left+1的差:若差 >1,则mismatch--。
- 窗口扩展(
-
记录最大窗口长度
遍历过程中记录max_len = max(max_len, right - left + 1)。
示例演算
nums = [1, 3, 2, 2, 5, 6, 4, 4], k=1
排序后:[1, 2, 2, 3, 4, 4, 5, 6]
left=0, right=0: 窗口[1], mismatch=0, len=1right=1: 加入 2,差=1(和谐),mismatch=0,窗口[1,2], len=2right=2: 加入 2,差=0(和谐),mismatch=0,窗口[1,2,2], len=3right=3: 加入 3,差=1(和谐),mismatch=0,窗口[1,2,2,3], len=4right=4: 加入 4,差=1(和谐),mismatch=0,窗口[1,2,2,3,4], len=5right=5: 加入 4,差=0(和谐),mismatch=0,窗口[1,2,2,3,4,4], len=6right=6: 加入 5,与4的差=1(和谐),但5与4的相邻对在窗口中实际是(4,5),差=1,和谐?- 纠正:在排序数组中,相邻指索引相邻,非原序列位置。此处
sorted[5]=4,sorted[6]=5,差=1,和谐。 - 继续:
right=7,加入6,与5的差=1,和谐。最终窗口包含全部8个元素,mismatch=0? - 错误:检查差>1的相邻对:例如
(3,4)差=1,但(4,5)差=1,(5,6)差=1,无差>1的相邻对? - 重新检查:在排序数组中,差>1的相邻对有哪些?
- 1→2:差1,2→2:差0,2→3:差1,3→4:差1,4→4:差0,4→5:差1,5→6:差1。
- 确实没有差>1的相邻对!但原例子序列
[2,2,5,6,4]在排序后为[2,2,4,5,6],其中(2,4)差=2>1,这是一个不和谐对。
- 关键点:排序后,子序列在原序列中的顺序可能被打乱,但题目要求子序列(不要求连续)只需保持原顺序。排序法仅适用于连续子数组问题,对子序列无效!
- 纠正:在排序数组中,相邻指索引相邻,非原序列位置。此处
回溯到动态规划解法
由于排序会破坏原顺序,正确解法仍为 O(n²k) 的 DP:
- 初始化
dp[i][0] = 1,max_len = 1。 - 对于每个
i从 0 到 n-1,每个j从 0 到 k:- 枚举
p从 0 到 i-1:- 若
|nums[i]-nums[p]| ≤ 1,则dp[i][j] = max(dp[i][j], dp[p][j] + 1) - 否则若
j ≥ 1,则dp[i][j] = max(dp[i][j], dp[p][j-1] + 1)
- 若
- 更新
max_len。
- 枚举
- 返回
max_len。
复杂度:时间 O(n²k),空间 O(nk)。可通过记录前缀最大值优化至 O(nk),但需额外处理。