Given an array nums
of integers, you are to find the length of its longest harmonious subsequence.
A harmonious subsequence is a subsequence where the difference between its maximum and minimum values is exactly 1.
Constraints:
nums
.nums = [1,3,2,2,5,2,3,7]
5
[3,2,2,2,3]
(or [2,3,2,2,3]
), where the max is 3 and min is 2, and the difference is 1.
At first glance, one might consider generating all possible subsequences and checking if their maximum and minimum values differ by exactly 1. However, this brute-force approach is highly inefficient because the number of subsequences grows exponentially with the size of the array.
To optimize, we need to notice that the order of elements in a subsequence is preserved, but we can pick or skip any element. The core of the problem is to count the maximum number of elements that can be picked where all values are either n
or n+1
(or n-1
), for some integer n
.
This leads us to focus on counting occurrences of each number and looking for pairs of numbers that differ by exactly 1. By summing the counts of such pairs, we can find the length of the longest harmonious subsequence.
Here’s a step-by-step breakdown of the optimized solution:
nums
. This allows for O(1) lookups and efficient aggregation.
n
in the hash map, check if n+1
also exists. If it does, the combined count of n
and n+1
represents a possible harmonious subsequence.
We use a hash map because it allows us to efficiently count and check for the presence of harmonious pairs in O(1) time per operation, making the overall approach much faster than brute-force.
Let’s walk through the sample input nums = [1,3,2,2,5,2,3,7]
:
count = {1:1, 2:3, 3:2, 5:1, 7:1}
Thus, the answer is 5
.
Brute-force approach: Generating all possible subsequences is O(2n), which is infeasible for large n
.
Optimized approach:
nums
. Counting occurrences takes O(n), and iterating through the unique numbers (at most n) is also O(n).
The key insight for solving the Longest Harmonious Subsequence problem is to recognize that we only need to count pairs of numbers that differ by 1 and sum their frequencies. Using a hash map for counting makes the solution both simple and efficient, reducing the problem from exponential to linear time. This approach is elegant because it leverages frequency counting and avoids unnecessary enumeration of subsequences.
from collections import Counter
class Solution:
def findLHS(self, nums):
count = Counter(nums)
max_len = 0
for num in count:
if num + 1 in count:
max_len = max(max_len, count[num] + count[num + 1])
return max_len
#include <unordered_map>
#include <vector>
#include <algorithm>
class Solution {
public:
int findLHS(std::vector<int>& nums) {
std::unordered_map<int, int> count;
for (int n : nums) {
count[n]++;
}
int max_len = 0;
for (const auto& p : count) {
int num = p.first;
if (count.find(num + 1) != count.end()) {
max_len = std::max(max_len, count[num] + count[num + 1]);
}
}
return max_len;
}
};
import java.util.HashMap;
class Solution {
public int findLHS(int[] nums) {
HashMap<Integer, Integer> count = new HashMap<>();
for (int n : nums) {
count.put(n, count.getOrDefault(n, 0) + 1);
}
int maxLen = 0;
for (int key : count.keySet()) {
if (count.containsKey(key + 1)) {
maxLen = Math.max(maxLen, count.get(key) + count.get(key + 1));
}
}
return maxLen;
}
}
var findLHS = function(nums) {
const count = {};
for (let n of nums) {
count[n] = (count[n] || 0) + 1;
}
let maxLen = 0;
for (let num in count) {
let n = parseInt(num);
if (count.hasOwnProperty(n + 1)) {
maxLen = Math.max(maxLen, count[n] + count[n + 1]);
}
}
return maxLen;
};