2Brute2Best
🔥0

Subarrays With At Most K Distinct Integers

Medium·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelVariable Sliding Window + Count Map, counting right − left + 1 per right end
Complexity:O(N²)→O(N)
Key Insight

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.

Core Validity Invariant

The window [left..right] is valid when it contains at most k distinct values, that is freq.size() ≤ k.

01

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.

Input

An integer array arr of length n, and an integer k.

Output

The number of subarrays arr[i..j] (with i ≤ j) that have at most k distinct values.

Example 1
Input: arr = [1, 2, 2, 3], k = 2
Output: 9

There are 10 subarrays in total. Only [1, 2, 2, 3] has 3 distinct values. The other 9 have at most 2.

Example 2
Input: arr = [1, 1, 1], k = 1
Output: 6

Every subarray contains only the value 1, so all 6 subarrays count.

Example 3
Input: arr = [1, 2, 1, 1, 3, 3, 4, 2, 1], k = 2
Output: 24

Counting the valid subarrays ending at each index gives 1 + 2 + 3 + 4 + 3 + 4 + 3 + 2 + 2 = 24.

Constraints
  • • 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.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    How many subarrays does an array of length n have? Can I afford to look at each one?

  2. 2.

    If a subarray has at most k distinct values, what about a shorter subarray inside it?

  3. 3.

    If a subarray already has more than k distinct values, can making it longer ever fix that?

  4. 4.

    Instead of counting subarrays by where they start, could I count them by where they end?

  5. 5.

    For a fixed right end, which starts give a valid subarray? Do they form one continuous block?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceFor each start, extend right with a set until it exceeds k distinct valuesO(N²)O(K)
OptimalKeep the longest valid window ending at right; it holds right − left + 1 valid subarraysO(N)O(K)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “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.
Count subarrays+"at most" rule that shrinking always fixes+count map for distinct values→Variable Sliding Window + Count Map, counting right − left + 1 per right end

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).

10

Don't make this mistake

Adding 1 instead of the window length
✗ count += 1; // or count++
✓ count += right - left + 1;

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.

Not erasing a value when its count reaches 0
✗ freq[arr[left]]--; left++;
✓ if (--freq[arr[left]] == 0) freq.erase(arr[left]); left++;

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.

Using if instead of while to shrink
✗ if (freq.size() > k) { /* remove arr[left] */ left++; }
✓ while (freq.size() > k) { /* remove arr[left] */ left++; }

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.

Confusing this with Exactly K

"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.

Counting at the wrong moment

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.

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Sliding Window translate into production architectures and low-level memory mechanics.