最长定差子序列
字数 1354 2025-11-13 09:54:06
最长定差子序列
我将为您讲解"最长定差子序列"这道线性动态规划题目。
题目描述
给定一个整数数组arr和一个整数difference,找出arr中最长的等差子序列的长度,其中等差数列的公差等于给定的difference。
例如:
输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是[7,5,3,1],公差为-2
解题思路
这是一个经典的线性动态规划问题。我们需要找到最长的子序列,使得相邻元素之间的差值正好等于给定的difference。
详细解题步骤
步骤1:定义状态
我们定义dp[i]表示以arr[i]结尾的最长等差子序列的长度。
步骤2:状态转移方程
对于每个位置i,我们需要找到前面所有满足arr[j] = arr[i] - difference的位置j,然后在这些位置中选择dp值最大的那个。
状态转移方程为:
dp[i] = max(dp[j] + 1),其中j < i且arr[j] = arr[i] - difference
如果找不到这样的j,那么dp[i] = 1
步骤3:优化状态转移
直接使用上述状态转移方程的时间复杂度是O(n²),我们可以用哈希表来优化:
使用哈希表freq,其中freq[x]表示以数值x结尾的最长等差子序列的长度。
对于每个元素arr[i]:
- 查找前一个元素prev = arr[i] - difference
- 如果prev在哈希表中,dp[i] = freq[prev] + 1
- 否则,dp[i] = 1
- 更新freq[arr[i]] = max(freq[arr[i]], dp[i])
步骤4:算法实现
def longestSubsequence(arr, difference):
dp = {} # 哈希表,存储以每个数值结尾的最长子序列长度
max_length = 0
for num in arr:
# 查找前一个元素是否存在
prev = num - difference
if prev in dp:
dp[num] = dp[prev] + 1
else:
dp[num] = 1
max_length = max(max_length, dp[num])
return max_length
步骤5:示例分析
以arr = [1,5,7,8,5,3,4,2,1], difference = -2为例:
- 处理1:dp[1] = 1
- 处理5:dp[5] = 1 (5-(-2)=7不在dp中)
- 处理7:dp[7] = 1 (7-(-2)=9不在dp中)
- 处理8:dp[8] = 1 (8-(-2)=10不在dp中)
- 处理5:dp[5] = 1 (5已存在,但5-(-2)=7不在dp中,保持1)
- 处理3:dp[3] = dp[5] + 1 = 2 (3-(-2)=5在dp中)
- 处理4:dp[4] = 1 (4-(-2)=6不在dp中)
- 处理2:dp[2] = dp[4] + 1 = 2 (2-(-2)=4在dp中)
- 处理1:dp[1] = dp[3] + 1 = 3 (1-(-2)=3在dp中)
最终找到最长子序列长度为4(对应序列[7,5,3,1])
步骤6:复杂度分析
- 时间复杂度:O(n),只需要遍历数组一次
- 空间复杂度:O(n),哈希表最多存储n个键值对
关键点总结
- 使用哈希表记录以每个数值结尾的最长子序列长度
- 对于每个元素,只需要检查前一个元素(当前值-difference)是否出现过
- 这种方法将时间复杂度从O(n²)优化到O(n)
这个解法巧妙地利用了哈希表来避免双重循环,是线性动态规划中空间换时间的典型应用。