You are given an integer array arr
and an integer difference
. Your task is to find the length of the longest subsequence in arr
such that the difference between adjacent elements in the subsequence is exactly difference
.
A subsequence is a sequence that can be derived from the array by deleting some or no elements without changing the order of the remaining elements. Elements cannot be reused.
Constraints:
arr.length
≤ 105arr[i]
, difference
≤ 104The solution must be efficient enough to handle large input sizes.
At first glance, we might consider checking all possible subsequences and filtering those that form an arithmetic sequence with the given difference
. However, generating all subsequences is computationally infeasible for large arrays.
Instead, we notice that for each element, if we know the length of the longest valid subsequence ending with arr[i] - difference
, we can extend it by including arr[i]
. This suggests a dynamic programming or hash map approach, where we "build up" the answer as we traverse the array.
The key insight is to efficiently track, for every possible value, the length of the longest subsequence ending at that value. This avoids redundant work and lets us process the array in a single pass.
To solve the problem efficiently, we use a hash map (dictionary) to keep track of the length of the longest arithmetic subsequence ending at each value.
dp
. For any value x
, dp[x]
stores the length of the longest subsequence ending at x
.
num
in arr
:
num - difference
exists in dp
. If it does, set dp[num] = dp[num - difference] + 1
.num
by setting dp[num] = 1
.dp
throughout the iteration. This will be the answer.
Why a hash map? Because it allows O(1) access to the subsequence length for any value, which is crucial for efficiency.
Input: arr = [1, 5, 7, 8, 5, 3, 4, 2, 1]
, difference = -2
Let's process the array step by step:
1 - (-2) = 3
not in dp
. Start new: dp[1] = 1
5 - (-2) = 7
not in dp
. Start new: dp[5] = 1
7 - (-2) = 9
not in dp
. Start new: dp[7] = 1
8 - (-2) = 10
not in dp
. Start new: dp[8] = 1
5 - (-2) = 7
in dp
(dp[7]=1
): dp[5] = 2
3 - (-2) = 5
in dp
(dp[5]=2
): dp[3] = 3
4 - (-2) = 6
not in dp
. Start new: dp[4] = 1
2 - (-2) = 4
in dp
(dp[4]=1
): dp[2] = 2
1 - (-2) = 3
in dp
(dp[3]=3
): dp[1] = 4
The maximum value in dp
is 4, corresponding to the subsequence [7, 5, 3, 1].
Brute-force approach:
n
.This efficiency makes the approach suitable for large input sizes.
By recognizing that the problem reduces to tracking the longest subsequence ending at each value, and using a hash map to do so efficiently, we avoid brute-force enumeration. This dynamic programming approach ensures that we only need a single pass through the array, making the solution both elegant and performant.
The main insight is to "build up" the answer for each value based on previously seen values, leveraging constant-time hash map lookups for maximum efficiency.
class Solution:
def longestSubsequence(self, arr, difference):
dp = {}
maxlen = 0
for num in arr:
prev = num - difference
dp[num] = dp.get(prev, 0) + 1
maxlen = max(maxlen, dp[num])
return maxlen
class Solution {
public:
int longestSubsequence(vector<int>& arr, int difference) {
unordered_map<int, int> dp;
int maxlen = 0;
for (int num : arr) {
int prev = num - difference;
dp[num] = dp.count(prev) ? dp[prev] + 1 : 1;
maxlen = max(maxlen, dp[num]);
}
return maxlen;
}
};
import java.util.HashMap;
class Solution {
public int longestSubsequence(int[] arr, int difference) {
HashMap<Integer, Integer> dp = new HashMap<>();
int maxlen = 0;
for (int num : arr) {
int prev = num - difference;
int currLen = dp.getOrDefault(prev, 0) + 1;
dp.put(num, currLen);
maxlen = Math.max(maxlen, currLen);
}
return maxlen;
}
}
var longestSubsequence = function(arr, difference) {
const dp = new Map();
let maxlen = 0;
for (const num of arr) {
const prev = num - difference;
const currLen = (dp.get(prev) || 0) + 1;
dp.set(num, currLen);
maxlen = Math.max(maxlen, currLen);
}
return maxlen;
};