最长和谐子序列的变种:最多允许k次不和谐的元素
字数 999 2025-11-23 08:09:31
最长和谐子序列的变种:最多允许k次不和谐的元素
题目描述
给定一个整数数组nums和一个整数k,我们需要找到最长的和谐子序列的长度。和谐子序列定义为:序列中最大值和最小值的差恰好为1。但是在这个变种问题中,我们允许最多k个元素不满足这个条件(即这些元素的值与序列中其他元素的差值可以大于1)。
例如:
- nums = [1,3,2,2,5,2,3,7], k = 1
- 输出:5
- 解释:最长和谐子序列可以是[3,2,2,2,3],其中最大值3和最小值2的差为1,且只有一个元素5不满足条件(k=1)
解题过程
步骤1:理解问题核心
和谐子序列要求序列中最大值和最小值的差正好为1。在变种问题中,我们允许最多k个"异常值"(这些元素的值不在[min, min+1]范围内)。
关键观察:
- 和谐序列实际上只包含两种数值:x和x+1
- 异常值可以是任何其他数值
- 我们需要找到一对相邻数值(x, x+1),使得包含这对数值的所有元素数量加上最多k个异常值能达到最大长度
步骤2:排序简化问题
由于我们关心的是数值本身而非原始顺序,可以先将数组排序:
sorted_nums = sorted(nums)
排序后,相同数值的元素会聚集在一起,便于我们统计频率和滑动窗口处理。
步骤3:频率统计
统计每个数值出现的频率:
# 例如 nums = [1,3,2,2,5,2,3,7], k = 1
# 频率统计:
# 1: 1次, 2: 3次, 3: 2次, 5: 1次, 7: 1次
步骤4:滑动窗口方法
我们使用滑动窗口来找到最优的数值对(x, x+1):
- 定义左指针left和右指针right
- 维护当前窗口中的数值集合
- 计算窗口内满足条件的元素数量
具体算法:
def findLHS(nums, k):
nums.sort()
left = 0
max_length = 0
counter = {}
for right in range(len(nums)):
# 更新当前数值的频率
counter[nums[right]] = counter.get(nums[right], 0) + 1
# 检查当前窗口是否有效
while max(counter.keys()) - min(counter.keys()) > 1:
# 窗口无效,移动左指针
counter[nums[left]] -= 1
if counter[nums[left]] == 0:
del counter[nums[left]]
left += 1
# 计算当前窗口的有效长度
current_length = right - left + 1
max_length = max(max_length, current_length)
return min(max_length, len(nums)) # 确保不超过数组长度
但上面的代码没有正确处理k个异常值的情况,我们需要改进。
步骤5:处理异常值的改进算法
更精确的算法:
def findLHSWithKExceptions(nums, k):
nums.sort()
left = 0
max_length = 0
exception_count = 0
for right in range(len(nums)):
# 如果当前元素与窗口最小值差值大于1,增加异常计数
if nums[right] - nums[left] > 1:
exception_count += 1
# 当异常值超过k时,移动左指针
while exception_count > k:
if nums[left + 1] - nums[left] > 0: # 如果左边界是较小值
exception_count -= 1
left += 1
# 更新最大长度
max_length = max(max_length, right - left + 1)
return max_length
步骤6:最终优化解法
更高效的解法是分别考虑每对相邻数值(x, x+1):
def findLHSWithKExceptions(nums, k):
from collections import Counter
count = Counter(nums)
unique_nums = sorted(count.keys())
max_length = 0
for i in range(len(unique_nums) - 1):
if unique_nums[i + 1] - unique_nums[i] == 1:
# 这对相邻数值可以形成和谐子序列的基础
base_length = count[unique_nums[i]] + count[unique_nums[i + 1]]
# 计算可以添加的异常值数量
remaining_k = k
current_length = base_length
# 尝试向两边扩展,添加异常值
# 向左扩展(小于min的值)
j = i - 1
while j >= 0 and remaining_k > 0:
can_add = min(remaining_k, count[unique_nums[j]])
current_length += can_add
remaining_k -= can_add
j -= 1
# 向右扩展(大于max的值)
j = i + 2
remaining_k = k - (base_length - current_length) # 重置剩余k值
while j < len(unique_nums) and remaining_k > 0:
can_add = min(remaining_k, count[unique_nums[j]])
current_length += can_add
remaining_k -= can_add
j += 1
max_length = max(max_length, current_length)
return max_length
步骤7:动态规划思路(备选)
我们也可以用动态规划来解决:
def findLHSWithKExceptions_DP(nums, k):
nums.sort()
n = len(nums)
# dp[i][j] 表示以nums[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):
for prev in range(i):
diff = nums[i] - nums[prev]
exceptions_used = 0 if diff <= 1 else 1
if j >= exceptions_used:
if diff <= 1 or exceptions_used == 1:
dp[i][j] = max(dp[i][j], dp[prev][j - exceptions_used] + 1)
max_len = max(max_len, dp[i][j])
return max_len
步骤8:复杂度分析
- 排序:O(n log n)
- 滑动窗口:O(n)
- 空间复杂度:O(n) 用于存储频率计数
步骤9:示例验证
以nums = [1,3,2,2,5,2,3,7], k = 1为例:
- 排序后:[1,2,2,2,3,3,5,7]
- 考虑数值对(2,3):基础长度 = 3(个2) + 2(个3) = 5
- 剩余k=1,可以添加1个异常值(5或1)
- 最终得到长度6:[1,2,2,2,3,3] 或 [2,2,2,3,3,5]
这个解法能够有效地找到最长的和谐子序列,同时允许最多k个异常元素。