给你一个整数数组 arr
和一个整数 difference
,请你找出并返回 arr
中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference
。
子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr
派生出来的序列。
示例 1:
输入:arr = [1,2,3,4], difference = 1 输出:4 解释:最长的等差子序列是 [1,2,3,4]。
示例 2:
输入:arr = [1,3,5,7], difference = 1 输出:1 解释:最长的等差子序列是任意单个元素。
示例 3:
输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2 输出:4 解释:最长的等差子序列是 [7,5,3,1]。
提示:
1 <= arr.length <= 10^5
-10^4 <= arr[i], difference <= 10^4
遇到最值问题,我们可以往动态规划方法去想一想,此题可以用动态规划来进行解题,但这题还需要加上哈希表。动规五部曲(dp含义、递推公式、初始化、遍历顺序、打印数组) ,主要的就是把大问题拆解成小问题,小问题的值保存下来,从小问题的答案推导到最终答案,空间换时间。
题目及思路:这题 一开始我把dp[i]的含义定义为arr数组每个位置i所能形成的最长定差子序列长度,想的是使用双重循环遍历每个i和j<i的情况,检查是否能将arr[i]接在arr[j]后面形成更长的定差子序列,从而更新dp[i]的值,但这样时间复杂度是O(n²),导致超时。
那只能优化这个思路,改用更高效的方法。使用哈希表来记录每个数值对应的最长序列长度。这样,对于每个元素num(arr[i]),只需要查找前驱值prev = num - difference是否存在,就能直接得到前面的长度,不需要遍历所有之前的元素。这样可以将时间复杂度降低到O(n)。
然而c语言没有直接能使用哈希表,所以我们可以创建dp数组dp[num],其中的num就是键值,这里的键值代表每个数值,dp[num]所得到的值表示以值 num
结尾的最长等差子序列长度。
这题我们需要特别注意看一下范围,看得出我们在查找prev = num - difference是否存在时会出现负数,从而可能导致下标为负数,所以我们先把num偏移到非负数,再计算前驱值prev。如果dp[prev]>0说明前驱值存在,则dp[num] = dp[prev]+1,else dp[num] = 1;(当前元素单独成序列)
dp含义:dp[num]表示以值 num
结尾的最长等差子序列长度。
初始化:int dp[200001]={0};初始化全为0.
递推公式:dp[num] = dp[prev] + 1;
遍历顺序:从前往后
打印数组:当遇到疑惑或者提交错误时,打印数组出来比较快速的看看哪一步有错。
这是没加100000的遍历过程,以方便大家观看和理解
以下是我在力扣c语言提交的代码,仅供参考:
int longestSubsequence(int* arr, int arrSize, int difference) {
// 初始化为全0
int dp[200001]={0};
int max = 0;
for(int i = 0;i<arrSize;i++)
{
int num = 0;
// 偏移到非负数
num = arr[i] + 100000;
int prev = num - difference;
// 检查 prev 是否在合法范围内和前驱值是否存在
if(prev >= 0 && prev < 200001 && dp[prev] > 0)
{
dp[num] = dp[prev] + 1;
}
else
{
// 前驱不存在,独立成序列
dp[num] = 1;
}
if(dp[num] > max)
{
max = dp[num];
}
}
return max;
}