Subarrays with K Different Integers
Problem Statement
Given an integer array nums and an integer k, return the number of contiguous subarrays that contain exactly k distinct integers.
Input
An integer array nums and an integer k, the exact number of distinct values a valid subarray must contain.
Output
An integer: the count of contiguous subarrays whose distinct-value count is exactly k.
Constraints
- •
1 <= nums.length <= 2 * 10^4 - •
1 <= nums[i], k <= nums.length
Examples
Example 1
nums = [1,2,1,2,3], k = 2
7Example 2
nums = [1,2,1,3,4], k = 3
3Example 3
nums = [1,1,1], k = 1
6Learning Objectives
- Recognise exact distinct-count subarray questions as candidates for **exactly(K) = atMost(K) - atMost(K - 1)**.
- Maintain a variable window with a frequency map and a bounded number of distinct values.
- Explain why every valid at-most window ending at **right** contributes **right - left + 1** subarrays.
- Convert an exact requirement into two monotone at-most counting passes.
Intuition
Pattern Identification
The phrase exactly k distinct values is the trap. A window with exactly k distinct values is not monotone: after you extend the right edge, it may become invalid, and after you shrink the left edge it may become valid again, but counting only exact windows directly is awkward.
The monotone version is at most k distinct values. If a window ending at right has at most k distinct values, every suffix of that window also has at most k distinct values. That gives the reusable identity exactly(K) = atMost(K) - atMost(K - 1). Count all subarrays with at most k distinct values, subtract those with at most k - 1, and what remains are the subarrays with exactly k distinct values.
Common mistakes
- ×Trying to count exact windows directly and adding only one when the window has exactly **k** distinct values.
- ×Forgetting that each valid at-most window ending at **right** contributes **right - left + 1** subarrays, not just one.
- ×Decrementing a frequency to zero but leaving the key in the map, so the distinct count is wrong.
- ×Using a fixed-size window even though the subarray length is unrestricted.
Algorithm Explanation
Window setup
Build a helper atMost(limit). It stores a frequency map for values inside nums[left..right]. The invariant is that the map contains at most limit distinct keys. When adding nums[right] creates too many distinct values, move left forward and decrease frequencies until the invariant is restored.
Window visualization
For nums = [1,2,1,2,3] and limit = 2, when right = 3, the valid window is [1,2,1,2] from indices 0..3. Every suffix ending at index 3 is valid: [2], [1,2], [2,1,2], and [1,2,1,2]. That is right - left + 1 = 4 new subarrays. When right = 4 adds 3, the window has three distinct values, so left advances until only [2,3] remains. Now this right edge adds 2 valid subarrays.
Algorithm
- Write atMost(limit) and return 0 immediately when limit < 0.
- Initialise left = 0, an empty frequency map, and total = 0.
- Expand right across the array, adding the new value to the frequency map.
- While the map has more than limit keys, remove nums[left] from the map and advance left.
- After the window is valid, add right - left + 1 because every suffix of the valid window ending at right is valid.
- Return atMost(k) - atMost(k - 1).
Solutions
Solution: At-most difference with frequency map
The exact requirement is handled by subtraction. The helper counts subarrays with at most a given number of distinct values using a standard variable window. Because at-most validity is preserved by taking suffixes, each right edge contributes all starts from left through right.
Step-by-step
- Compute atMost(nums, k) and atMost(nums, k - 1).
- In the helper, add nums[right] to the frequency map.
- If the number of keys exceeds the limit, move left forward and delete keys whose frequency drops to zero.
- Once the invariant is restored, add right - left + 1 to the helper answer.
- Subtract the two helper counts to isolate subarrays with exactly k distinct values.
O(n)
O(k)
Each helper pass moves left and right forward at most n times. The map stores at most k plus one distinct values during shrinking.
Java implementation
Dry Run
Sample input
nums = [1,2,1,2,3], k = 2. Count atMost(2) and atMost(1), then subtract them to get exactly 2 distinct values.
| phase | right | value | left after shrink | window state | subarrays added | running count | meaning |
|---|---|---|---|---|---|---|---|
| atMost(2) | 0 | 1 | 0 | {1:1} | 1 | 1 | [1] |
| atMost(2) | 1 | 2 | 0 | {1:1, 2:1} | 2 | 3 | [2], [1,2] |
| atMost(2) | 2 | 1 | 0 | {1:2, 2:1} | 3 | 6 | three valid suffixes |
| atMost(2) | 3 | 2 | 0 | {1:2, 2:2} | 4 | 10 | four valid suffixes |
| atMost(2) | 4 | 3 | 3 | {2:1, 3:1} | 2 | 12 | shrink past the extra distinct value |
| atMost(1) | 0 | 1 | 0 | {1:1} | 1 | 1 | single value only |
| atMost(1) | 1 | 2 | 1 | {2:1} | 1 | 2 | shrink away 1 |
| atMost(1) | 2 | 1 | 2 | {1:1} | 1 | 3 | shrink away 2 |
| atMost(1) | 3 | 2 | 3 | {2:1} | 1 | 4 | shrink away 1 |
| atMost(1) | 4 | 3 | 4 | {3:1} | 1 | 5 | shrink away 2 |
The helper counts are atMost(2) = 12 and atMost(1) = 5. Their difference is 7, which is the number of subarrays with exactly 2 distinct integers.
Interview Tips
Lead with the identity exactly(K) = atMost(K) - atMost(K - 1). Then justify why atMost is easy: once the window has at most K distinct values, all suffixes ending at the current right edge are also valid. Interviewers look for that counting jump; without it, candidates often write a complicated exact-window loop.
Likely follow-ups
- How would the helper change if values were small enough to use an array instead of a hash map?
- How would you count subarrays with exactly **k** odd numbers?
- How would you count subarrays with at most **k** distinct values and also return one longest example?
- Why does the exact trick work for counts but not directly for longest-window answers?
Similar Problems
Key Takeaways
- Exact distinct-count windows become simple when rewritten as two at-most counts.
- For at-most counting, a valid window ending at **right** contributes **right - left + 1** subarrays.
- Deleting zero-frequency keys is required to keep the distinct count accurate.
- The two-pointer movement is linear because **left** and **right** only move forward.