Maximum Sum of Distinct Subarrays With Length K
MedPrompt
You are given an integer array nums and an integer k. Find the maximum subarray sum of all the subarrays of nums that meet the following conditions:
- The length of the subarray is
k, and - All the elements of the subarray are distinct.
Return the maximum subarray sum of all the subarrays that meet the conditions. If no subarray meets the conditions, return 0.
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
Input: nums = [1,5,4,2,9,9,9], k = 3
Output: 15
Explanation: The subarrays of nums with length 3 are:
- [1,5,4] which meets the requirements and has a sum of 10.
- [5,4,2] which meets the requirements and has a sum of 11.
- [4,2,9] which meets the requirements and has a sum of 15.
- [2,9,9] which does not meet the requirements because the element 9 is repeated.
- [9,9,9] which does not meet the requirements because the element 9 is repeated.
We return 15 because it is the maximum subarray sum of all the subarrays that meet the conditionsExample 2:
Input: nums = [4,4,4], k = 3
Output: 0
Explanation: The subarrays of nums with length 3 are:
- [4,4,4] which does not meet the requirements because the element 4 is repeated.
We return 0 because no subarrays meet the conditions.
Constraints:
1 <= k <= nums.length <= 1051 <= nums[i] <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses a sliding window of size k to efficiently calculate the sum and check for distinct elements. By sliding the window one element at a time, we can update the sum and element counts in constant time on average, avoiding recalculation.
Algorithm
- Initialize
maxSum = 0L,currentSum = 0L, and aHashMap<Integer, Integer> freqMapto store element frequencies in the current window. - Iterate through the
numsarray with indexifrom0ton-1. - Add element to window: Add
nums[i]tocurrentSumand increment its count infreqMap. - Maintain window size: If
i >= k, the window is now of sizek+1. We must remove the leftmost element,nums[i-k], to shrink it back to sizek.- Subtract
nums[i-k]fromcurrentSum. - Decrement the count of
nums[i-k]infreqMap. - If the count of
nums[i-k]becomes 0, remove it from the map to keep the map size accurate for the distinctness check.
- Subtract
- Check for valid subarray: If the window has reached size
k(i.e.,i >= k-1), check if it meets the distinctness condition.- The condition is met if
freqMap.size() == k. - If it is met, update
maxSum = Math.max(maxSum, currentSum).
- The condition is met if
- After the loop finishes, return
maxSum.
Walkthrough
This optimized approach avoids the redundant work of the brute-force method by using a sliding window. The window is a conceptual frame of size k that moves across the array one element at a time. We maintain the sum of elements and their frequencies within the current window.
We use a HashMap to store the frequency of each number in the window. A key property we use is that a window of k elements has all distinct numbers if and only if the number of unique keys in our frequency map is exactly k.
The process begins by iterating through the array. For each element, we add it to our window. Once the window reaches size k, we check if it's a valid subarray. Then, for all subsequent elements, we "slide" the window by:
- Adding the new element from the right to our sum and frequency map.
- Removing the leftmost element (which is now outside the window) from our sum and frequency map.
After each slide, we check if the map's size is k. If it is, we have a valid subarray, and we update our maximum sum. This way, each element of the input array is processed only a constant number of times, leading to a linear time complexity.
import java.util.HashMap;import java.util.Map; class Solution { public long maximumSubarraySum(int[] nums, int k) { long maxSum = 0; long currentSum = 0; Map<Integer, Integer> freqMap = new HashMap<>(); int n = nums.length; for (int i = 0; i < n; i++) { // Add the current element to the window currentSum += nums[i]; freqMap.put(nums[i], freqMap.getOrDefault(nums[i], 0) + 1); // If window size is now greater than k, remove the leftmost element if (i >= k) { int leftElement = nums[i - k]; currentSum -= leftElement; freqMap.put(leftElement, freqMap.get(leftElement) - 1); if (freqMap.get(leftElement) == 0) { freqMap.remove(leftElement); } } // Check if the current window is valid (size k and all distinct) if (i >= k - 1) { if (freqMap.size() == k) { maxSum = Math.max(maxSum, currentSum); } } } return maxSum; }}Complexity
Time
O(n) - We iterate through the array a single time. All operations within the loop (map updates, arithmetic) take constant time on average.
Space
O(k) - The `HashMap` stores at most `k` key-value pairs, corresponding to the elements in the current window.
Trade-offs
Pros
Highly efficient with O(n) time complexity, making it suitable for large inputs.
Optimal solution for the given constraints.
Cons
Slightly more complex to implement compared to the brute-force approach.
Requires extra space for the frequency map, which could be O(k).
This approach involves iterating through every possible subarray of length k, checking if it contains distinct elements, and if so, calculating its sum. The maximum sum found is then returned.
Algorithm
- Initialize a variable
maxSumto 0. - Iterate through the array
numswith an indexifrom0ton-k, wherenis the length ofnums. This loop defines the starting position of each subarray. - For each
i, create a newHashSetto store the elements of the current subarray and acurrentSumvariable initialized to 0. - Start an inner loop with index
jfromitoi + k - 1. - Inside the inner loop, check if
nums[j]can be added to theHashSet. Theaddmethod returnsfalseif the element is already present. - If a duplicate is found, this subarray is invalid. Stop processing it and move to the next starting position
i. - If the element is unique, add it to the set and add its value to
currentSum. - After the inner loop finishes, if no duplicates were found (i.e., the loop completed for all
kelements), the subarray is valid. ComparecurrentSumwithmaxSumand updatemaxSumifcurrentSumis larger. - After the outer loop completes, return
maxSum.
Walkthrough
The logic is to check every single subarray of length k. We can use a nested loop structure. The outer loop defines the starting point of the subarray, and the inner loop iterates through the k elements of that subarray.
To check for uniqueness within the k elements, a HashSet is an effective tool. For each subarray, we create a new HashSet. As we iterate through its elements, we add them to the set and also accumulate their sum. If we try to add an element that's already present in the set, we know the subarray has duplicates. In this case, we can immediately discard this subarray and move to the next one.
If the inner loop completes without finding any duplicates, it means all k elements are distinct. We then compare its sum with the maximum sum found so far and update it if necessary. This process is repeated for all n-k+1 possible subarrays.
import java.util.HashSet;import java.util.Set; class Solution { public long maximumSubarraySum(int[] nums, int k) { long maxSum = 0; int n = nums.length; for (int i = 0; i <= n - k; i++) { Set<Integer> distinctElements = new HashSet<>(); long currentSum = 0; boolean hasDuplicates = false; // Check the subarray nums[i...i+k-1] for (int j = i; j < i + k; j++) { if (!distinctElements.add(nums[j])) { hasDuplicates = true; break; } currentSum += nums[j]; } if (!hasDuplicates) { maxSum = Math.max(maxSum, currentSum); } } return maxSum; }}Complexity
Time
O(n * k) - The outer loop runs `n-k+1` times, and for each iteration, the inner loop runs `k` times. This results in a quadratic time complexity relative to the input size and `k`.
Space
O(k) - In each iteration of the outer loop, a `HashSet` is created which can store up to `k` elements.
Trade-offs
Pros
Simple to understand and implement.
Directly translates the problem statement into code.
Cons
Highly inefficient, with a time complexity of O(n*k), which will be too slow for large inputs.
Performs a lot of redundant work. For each subarray, it re-calculates the sum and re-checks for distinct elements from scratch, even for overlapping parts.
Solutions
Solution
public class Solution { public long MaximumSubarraySum(int[] nums, int k) { int n = nums.Length; Dictionary < int, int > cnt = new Dictionary < int, int > (k); long s = 0; for (int i = 0; i < k; ++i) { if (!cnt.ContainsKey(nums[i])) { cnt[nums[i]] = 1; } else { cnt[nums[i]]++; } s += nums[i]; } long ans = cnt.Count == k ? s : 0; for (int i = k; i < n; ++i) { if (!cnt.ContainsKey(nums[i])) { cnt[nums[i]] = 1; } else { cnt[nums[i]]++; } if (--cnt[nums[i - k]] == 0) { cnt.Remove(nums[i - k]); } s += nums[i] - nums[i - k]; if (cnt.Count == k) { ans = Math.Max(ans, s); } } return ans; }}class Solution {public long maximumSubarraySum(int[] nums, int k) { int n = nums.length; Map<Integer, Integer> cnt = new HashMap<>(k); long s = 0; for (int i = 0; i < k; ++i) { cnt.merge(nums[i], 1, Integer : : sum); s += nums[i]; } long ans = cnt.size() == k ? s : 0; for (int i = k; i < n; ++i) { cnt.merge(nums[i], 1, Integer : : sum); s += nums[i]; if (cnt.merge(nums[i - k], -1, Integer : : sum) == 0) { cnt.remove(nums[i - k]); } s -= nums[i - k]; if (cnt.size() == k) { ans = Math.max(ans, s); } } return ans; }}class Solution {public: long long maximumSubarraySum(vector<int> &nums, int k) { using ll = long long; int n = nums.size(); unordered_map<int, ll> cnt; ll s = 0; for (int i = 0; i < k; ++i) { cnt[nums[i]]++; s += nums[i]; } ll ans = cnt.size() == k ? s : 0; for (int i = k; i < n; ++i) { cnt[nums[i]]++; s += nums[i]; cnt[nums[i - k]]--; s -= nums[i - k]; if (cnt[nums[i - k]] == 0) { cnt.erase(nums[i - k]); } if (cnt.size() == k) { ans = max(ans, s); } } return ans; }};class Solution: def maximumSubarraySum(self, nums: List[int], k: int) -> int: cnt = Counter(nums[: k]) s = sum(nums[: k]) ans = s if len(cnt) == k else 0 for i in range(k, len(nums)): cnt[nums[i]] += 1 s += nums[i] cnt[nums[i - k]] -= 1 s -= nums[i - k] if cnt[nums[i - k]] == 0: del cnt[nums[i - k]] if len(cnt) == k: ans = max(ans, s) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.