Subarrays With At Most K Distinct Integers
Move right across the array. For each right, keep left at the smallest start that still gives at most k distinct values. Then every start from left to right works, so exactly right − left + 1 valid subarrays end at right. Add that number and move on.
The window [left..right] is valid when it contains at most k distinct values, that is freq.size() ≤ k.
Problem
You are given an array of integers arr and a number k.
Count how many contiguous subarrays contain at most k different values. A value that appears several times inside a subarray still counts as one distinct value.
Implement int countAtMostK(vector<int>& arr, int k) inside class Solution. It returns the count as an int.
An integer array arr of length n, and an integer k.
The number of subarrays arr[i..j] (with i ≤ j) that have at most k distinct values.
There are 10 subarrays in total. Only [1, 2, 2, 3] has 3 distinct values. The other 9 have at most 2.
Every subarray contains only the value 1, so all 6 subarrays count.
Counting the valid subarrays ending at each index gives 1 + 2 + 3 + 4 + 3 + 4 + 3 + 2 + 2 = 24.
- • 1 ≤ n ≤ 2·10^4
- • 1 ≤ k ≤ 2·10^4
- • 1 ≤ arr[i] ≤ 10^9
- • The answer is at most n(n+1)/2 ≈ 2·10^8, which fits in an int.
First, think yourself
Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.
- 1.
How many subarrays does an array of length n have? Can I afford to look at each one?
- 2.
If a subarray has at most k distinct values, what about a shorter subarray inside it?
- 3.
If a subarray already has more than k distinct values, can making it longer ever fix that?
- 4.
Instead of counting subarrays by where they start, could I count them by where they end?
- 5.
For a fixed right end, which starts give a valid subarray? Do they form one continuous block?
Brute force approach
Optimized approach
Why does this algorithm work?
Dry run
All approaches compared
| Approach | Idea | Time | Space |
|---|---|---|---|
| Brute Force | For each start, extend right with a set until it exceeds k distinct values | O(N²) | O(K) |
| Optimal | Keep the longest valid window ending at right; it holds right − left + 1 valid subarrays | O(N) | O(K) |
Why does this belong to this pattern?
- “count subarrays”The answer is a count, not a length. Each step will add a number to a running total instead of taking a max.
- “at most k distinct”Monotonic rule: a shorter subarray never has more distinct values. Shrinking always helps, so a sliding window works.
- “distinct integers”We need to know how many different values are in the window → count map, and its size is the distinct count.
This is the anchor for the whole At Most K sub-pattern. The window logic is exactly Fruit Into Baskets / Longest Substring with K Distinct. The only change is the last line: instead of best = max(best, right − left + 1), we do count += right − left + 1. Later questions in this sub-pattern swap the rule (product, all three letters, max appears k times, sum × length), but keep this counting idea. It is also the building block for Exactly K: exactly(k) = atMost(k) − atMost(k − 1).
Don't make this mistake
The window [left..right] is not one subarray to count — it holds right − left + 1 valid subarrays that all end at right. Adding 1 only counts the longest one.
freq.size() counts keys, even keys with value 0. If you leave zero entries in the map, the distinct count stays too high and the window shrinks too much.
Removing one element may not remove a distinct value (another copy may still be inside). You may need to move left several times before the window is valid again.
"At most k distinct" is monotonic, so one window answers it. "Exactly k distinct" is not: a window with exactly k values can lose that property when it shrinks. Don't try to count exactly-k with one window — compute atMost(k) − atMost(k − 1) using this function twice.
Add right − left + 1 only after the while loop has made the window valid. If you add before shrinking, you count starts that are actually invalid.
Want to implement this?
Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.
Open coding workspaceHow this concept connects to System Design & C++ Systems
Top engineering interviews test whether you understand how algorithms like Sliding Window translate into production architectures and low-level memory mechanics.