2461. Maximum Sum of Distinct Subarrays With Length K
Problem Description
You have an integer array nums
and an integer k
. Your task is to find the maximum sum among all subarrays that satisfy two specific conditions:
- The subarray must have exactly
k
elements - All elements in the subarray must be distinct (no duplicates)
A subarray is a contiguous sequence of elements from the original array. For example, if nums = [1, 2, 3]
, then [1, 2]
and [2, 3]
are subarrays, but [1, 3]
is not.
You need to examine all possible subarrays of length k
and check if they contain only unique elements. Among all valid subarrays (those with distinct elements), return the one with the maximum sum. If no subarray of length k
has all distinct elements, return 0
.
For example:
-
If
nums = [1, 5, 4, 2, 9, 9, 9]
andk = 3
, the subarrays of length 3 are:[1, 5, 4]
,[5, 4, 2]
,[4, 2, 9]
,[2, 9, 9]
, and[9, 9, 9]
. Only[1, 5, 4]
,[5, 4, 2]
, and[4, 2, 9]
have all distinct elements. Their sums are 10, 11, and 15 respectively, so the answer is 15. -
If
nums = [4, 4, 4]
andk = 3
, the only subarray[4, 4, 4]
contains duplicate elements, so the answer is 0.
Intuition
The key insight is that we need to check every possible subarray of length k
to find the one with maximum sum that has all distinct elements. Since we're looking at consecutive subarrays of fixed length, this naturally suggests using a sliding window approach.
Think of it like a window of size k
that slides over the array from left to right. Initially, the window covers elements from index 0
to k-1
. Then we slide it one position to the right, removing the leftmost element and adding a new element from the right.
To efficiently check if all elements in the current window are distinct, we need to track the frequency of each element. A hash table (or Counter in Python) is perfect for this - we can maintain counts of all elements currently in the window. When all elements are distinct, each element appears exactly once, so the size of our hash table equals k
.
As we slide the window:
- Add the new element entering the window (increment its count)
- Remove the element leaving the window (decrement its count)
- If an element's count drops to 0, remove it from the hash table entirely
- Update the window sum by adding the new element and subtracting the removed element
The beauty of this approach is that we don't need to recalculate the sum from scratch for each window - we can update it incrementally by just adjusting for the element that entered and the element that left. Similarly, we don't need to rebuild the frequency map each time; we just update it for the two elements that changed.
Whenever the hash table size equals k
(meaning all elements are distinct), we check if the current sum is larger than our maximum found so far.
Learn more about Sliding Window patterns.
Solution Approach
We implement a sliding window technique combined with a hash table to efficiently track distinct elements and their counts.
Initial Setup:
- Create a Counter
cnt
to store the frequency of elements in the first window (elements from index0
tok-1
) - Calculate the initial sum
s
of the firstk
elements - Initialize the answer
ans
- if the first window has all distinct elements (checked bylen(cnt) == k
), setans = s
, otherwiseans = 0
Sliding the Window:
Starting from index k
, we iterate through the rest of the array. For each step:
-
Add the new element:
cnt[nums[i]] += 1
- increment the count of the element entering the windows += nums[i]
- add it to the running sum
-
Remove the old element:
cnt[nums[i - k]] -= 1
- decrement the count of the element leaving the window- If the count becomes 0, remove it from the hash table:
cnt.pop(nums[i - k])
s -= nums[i - k]
- subtract it from the running sum
-
Check validity and update answer:
- If
len(cnt) == k
, all elements in the current window are distinct - Update
ans = max(ans, s)
if the current sum is larger
- If
Why this works:
- The Counter maintains exact frequencies of elements in the current window
- When
len(cnt) == k
, it means we have exactlyk
different elements, each appearing once - The running sum
s
is maintained incrementally by adding the new element and subtracting the old one, avoiding recalculation - Time complexity is
O(n)
wheren
is the length of the array, as we visit each element once - Space complexity is
O(k)
for storing at mostk
elements in the Counter
The algorithm efficiently finds the maximum sum among all valid subarrays without having to recompute everything for each window position.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [1, 5, 4, 2, 9, 9, 9]
and k = 3
.
Initial Setup (First Window: [1, 5, 4])
- Create Counter:
cnt = {1: 1, 5: 1, 4: 1}
- Calculate initial sum:
s = 1 + 5 + 4 = 10
- Check if valid:
len(cnt) = 3 = k
✓ (all elements distinct) - Initialize answer:
ans = 10
Slide 1: Move to [5, 4, 2] (i = 3)
- Add new element
nums[3] = 2
:cnt[2] += 1
→cnt = {1: 1, 5: 1, 4: 1, 2: 1}
s = 10 + 2 = 12
- Remove old element
nums[0] = 1
:cnt[1] -= 1
→ count becomes 0, so remove from cntcnt = {5: 1, 4: 1, 2: 1}
s = 12 - 1 = 11
- Check validity:
len(cnt) = 3 = k
✓ - Update answer:
ans = max(10, 11) = 11
Slide 2: Move to [4, 2, 9] (i = 4)
- Add new element
nums[4] = 9
:cnt[9] += 1
→cnt = {5: 1, 4: 1, 2: 1, 9: 1}
s = 11 + 9 = 20
- Remove old element
nums[1] = 5
:cnt[5] -= 1
→ count becomes 0, so remove from cntcnt = {4: 1, 2: 1, 9: 1}
s = 20 - 5 = 15
- Check validity:
len(cnt) = 3 = k
✓ - Update answer:
ans = max(11, 15) = 15
Slide 3: Move to [2, 9, 9] (i = 5)
- Add new element
nums[5] = 9
:cnt[9] += 1
→cnt = {4: 1, 2: 1, 9: 2}
s = 15 + 9 = 24
- Remove old element
nums[2] = 4
:cnt[4] -= 1
→ count becomes 0, so remove from cntcnt = {2: 1, 9: 2}
s = 24 - 4 = 20
- Check validity:
len(cnt) = 2 ≠ k
✗ (has duplicates) - Answer remains:
ans = 15
Slide 4: Move to [9, 9, 9] (i = 6)
- Add new element
nums[6] = 9
:cnt[9] += 1
→cnt = {2: 1, 9: 3}
s = 20 + 9 = 29
- Remove old element
nums[3] = 2
:cnt[2] -= 1
→ count becomes 0, so remove from cntcnt = {9: 3}
s = 29 - 2 = 27
- Check validity:
len(cnt) = 1 ≠ k
✗ (has duplicates) - Answer remains:
ans = 15
Final Result: 15 (from the subarray [4, 2, 9])
Solution Implementation
1class Solution:
2 def maximumSubarraySum(self, nums: List[int], k: int) -> int:
3 # Initialize a counter to track element frequencies in the current window
4 element_count = Counter(nums[:k])
5
6 # Calculate the sum of the first k elements
7 current_sum = sum(nums[:k])
8
9 # Initialize the answer: if first window has all distinct elements, use its sum, otherwise 0
10 max_sum = current_sum if len(element_count) == k else 0
11
12 # Slide the window through the rest of the array
13 for i in range(k, len(nums)):
14 # Add the new element to the window
15 element_count[nums[i]] += 1
16
17 # Remove the leftmost element from the window
18 element_count[nums[i - k]] -= 1
19
20 # If count becomes 0, remove the element from the counter entirely
21 if element_count[nums[i - k]] == 0:
22 element_count.pop(nums[i - k])
23
24 # Update the current window sum
25 current_sum += nums[i] - nums[i - k]
26
27 # If all elements in the window are distinct, update the maximum sum
28 if len(element_count) == k:
29 max_sum = max(max_sum, current_sum)
30
31 return max_sum
32
1class Solution {
2 public long maximumSubarraySum(int[] nums, int k) {
3 int n = nums.length;
4
5 // HashMap to track frequency of elements in current window
6 Map<Integer, Integer> frequencyMap = new HashMap<>(k);
7
8 // Initialize the first window of size k
9 long currentSum = 0;
10 for (int i = 0; i < k; i++) {
11 frequencyMap.merge(nums[i], 1, Integer::sum);
12 currentSum += nums[i];
13 }
14
15 // If all k elements are distinct, set initial answer to current sum, otherwise 0
16 long maxSum = (frequencyMap.size() == k) ? currentSum : 0;
17
18 // Slide the window through the rest of the array
19 for (int i = k; i < n; i++) {
20 // Add the new element to the window
21 frequencyMap.merge(nums[i], 1, Integer::sum);
22
23 // Remove the leftmost element from the window
24 int leftElement = nums[i - k];
25 if (frequencyMap.merge(leftElement, -1, Integer::sum) == 0) {
26 frequencyMap.remove(leftElement);
27 }
28
29 // Update the current window sum
30 currentSum = currentSum + nums[i] - nums[i - k];
31
32 // If all elements in window are distinct, update max sum
33 if (frequencyMap.size() == k) {
34 maxSum = Math.max(maxSum, currentSum);
35 }
36 }
37
38 return maxSum;
39 }
40}
41
1class Solution {
2public:
3 long long maximumSubarraySum(vector<int>& nums, int k) {
4 using ll = long long;
5 int n = nums.size();
6
7 // Hash map to track frequency of elements in current window
8 unordered_map<int, ll> frequencyMap;
9
10 // Current window sum
11 ll windowSum = 0;
12
13 // Initialize the first window of size k
14 for (int i = 0; i < k; ++i) {
15 frequencyMap[nums[i]]++;
16 windowSum += nums[i];
17 }
18
19 // Initialize answer: if first window has all distinct elements, use its sum
20 ll maxSum = (frequencyMap.size() == k) ? windowSum : 0;
21
22 // Slide the window through the rest of the array
23 for (int i = k; i < n; ++i) {
24 // Add new element to the window
25 frequencyMap[nums[i]]++;
26
27 // Remove the leftmost element from the window
28 int elementToRemove = nums[i - k];
29 frequencyMap[elementToRemove]--;
30 if (frequencyMap[elementToRemove] == 0) {
31 frequencyMap.erase(elementToRemove);
32 }
33
34 // Update window sum
35 windowSum = windowSum + nums[i] - nums[i - k];
36
37 // If window has exactly k distinct elements, update maximum sum
38 if (frequencyMap.size() == k) {
39 maxSum = max(maxSum, windowSum);
40 }
41 }
42
43 return maxSum;
44 }
45};
46
1/**
2 * Finds the maximum sum of a subarray of length k with all distinct elements
3 * @param nums - The input array of numbers
4 * @param k - The length of the subarray
5 * @returns The maximum sum of a subarray of length k with all distinct elements, or 0 if no such subarray exists
6 */
7function maximumSubarraySum(nums: number[], k: number): number {
8 const arrayLength = nums.length;
9 // Map to track frequency of elements in current window
10 const frequencyMap: Map<number, number> = new Map();
11 let currentWindowSum = 0;
12
13 // Initialize first window of size k
14 for (let i = 0; i < k; ++i) {
15 frequencyMap.set(nums[i], (frequencyMap.get(nums[i]) ?? 0) + 1);
16 currentWindowSum += nums[i];
17 }
18
19 // Check if first window has all distinct elements (map size equals k)
20 let maxSum = frequencyMap.size === k ? currentWindowSum : 0;
21
22 // Slide the window through the rest of the array
23 for (let i = k; i < arrayLength; ++i) {
24 // Add new element to the window
25 frequencyMap.set(nums[i], (frequencyMap.get(nums[i]) ?? 0) + 1);
26
27 // Remove the leftmost element from the window
28 const leftElement = nums[i - k];
29 frequencyMap.set(leftElement, frequencyMap.get(leftElement)! - 1);
30
31 // If frequency becomes 0, remove the element from map
32 if (frequencyMap.get(leftElement) === 0) {
33 frequencyMap.delete(leftElement);
34 }
35
36 // Update current window sum
37 currentWindowSum += nums[i] - nums[i - k];
38
39 // If all elements in window are distinct, update maximum sum
40 if (frequencyMap.size === k) {
41 maxSum = Math.max(maxSum, currentWindowSum);
42 }
43 }
44
45 return maxSum;
46}
47
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the array nums
.
The algorithm uses a sliding window approach with a fixed window size k
. Initially, it processes the first k
elements to build the counter and calculate the initial sum, which takes O(k)
time. Then it iterates through the remaining n - k
elements once. In each iteration, it performs constant-time operations:
- Adding and updating a counter entry:
O(1)
average case for dictionary operations - Removing a counter entry if needed:
O(1)
average case - Updating the running sum:
O(1)
- Checking the counter size and updating the maximum:
O(1)
Since k ≤ n
, the total time complexity is O(k) + O(n - k) = O(n)
.
Space Complexity: O(k)
, which can be considered O(n)
in the worst case when k = n
.
The space is primarily used by the Counter
object cnt
, which stores at most k
distinct elements at any given time (since we're maintaining a sliding window of size k
). In the worst case where all elements in the window are distinct, the counter will have exactly k
entries. Since k
can be at most n
, the space complexity is O(min(k, n))
, which simplifies to O(n)
in the worst case.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle the Initial Window Properly
A common mistake is not checking if the first window (indices 0 to k-1) contains all distinct elements before starting the sliding process. This can lead to missing a valid maximum sum if it occurs in the first window.
Incorrect approach:
max_sum = 0 # Always starting with 0, ignoring the first window
for i in range(k, len(nums)):
# sliding window logic...
Correct approach:
# Check the first window explicitly
max_sum = current_sum if len(element_count) == k else 0
2. Incorrectly Managing the Counter When Removing Elements
When an element leaves the window, simply decrementing its count isn't enough. If the count reaches zero, the element must be completely removed from the counter. Otherwise, len(element_count)
will give an incorrect count of distinct elements.
Incorrect approach:
element_count[nums[i - k]] -= 1 # Forgetting to remove the key when count becomes 0
Correct approach:
element_count[nums[i - k]] -= 1 if element_count[nums[i - k]] == 0: element_count.pop(nums[i - k])
3. Using a Set Instead of a Counter
Some might think to use a set to track distinct elements, but this fails when the same element appears multiple times in the window.
Incorrect approach:
window_set = set(nums[:k])
# This loses frequency information
Why it fails: If nums = [1, 2, 3, 1, 5]
and k = 3
, the window [3, 1, 5]
would appear to have 3 distinct elements in a set, but we wouldn't know that when we slide to include the second 1
, we now have a duplicate.
4. Off-by-One Errors in Window Boundaries
Ensuring the window contains exactly k elements and correctly identifying which element to add and remove can be tricky.
Common mistake:
# Wrong indices for removal element_count[nums[i - k + 1]] -= 1 # Should be nums[i - k]
Correct indexing:
- When at index
i
, the window spans fromi - k + 1
toi
- The element to remove is at index
i - k
- The element to add is at index
i
Which of these pictures shows the visit order of a depth-first search?
Recommended Readings
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!