Algorithm

Subarrays with K Different Integers

Sliding Window Pattern

Subarrays with K Different Integers

Given an integer array nums and an integer k, return the number of good subarrays of nums. A good subarray is a subarray where the number of different integers in that subarray is exactly k.

CONSTRAINTS
  • 1 <= nums.length <= 2 × 10⁴
  • 1 <= nums[i], k <= nums.length
EXAMPLE 1
Input: nums = [1,2,1,2,3], k = 2
Output: 7
Subarrays with exactly 2 distinct integers: [1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2]. atMost(2)=12, atMost(1)=5, 12-5=7.
EXAMPLE 2
Input: nums = [1,2,1,3,4], k = 3
Output: 3
Subarrays with exactly 3 distinct integers: [1,2,1,3], [2,1,3], [1,3,4]. atMost(3)-atMost(2)=3.
EXAMPLE 3
Input: nums = [1,1,1,1], k = 1
Output: 10
All N*(N+1)/2 = 10 subarrays contain exactly 1 distinct integer. atMost(1) = 10, atMost(0) = 0, difference = 10.
Does 'exactly K distinct' mean no more and no fewer than K different values?
Correct. A subarray with 1 or 3 distinct values does not count when K=2. Exactly K means precisely K distinct integers.
Can a single element count as a valid subarray?
Yes. A single element has exactly 1 distinct integer. It counts when K=1.

Count the contiguous subarrays that hold exactly k distinct integers. Two things make this harder than the windows before it: we must count all of them (not find one best), and the condition is "exactly", which — as flagged back in Longest Substring Without Repeating Characters — is the case where the plain sliding window breaks.

Why "exactly k" won't drive a window

A window works only when the rule is one-directional at the edges. "At most k distinct" is: adding a new value can only raise the distinct-count (toward invalid), removing on the left can only lower it (toward valid) — clean, one-directional, exactly the Fruit Into Baskets rule with a general k. But "exactly k" is not: extending right might keep you at k or jump to k+1, and shrinking might keep you at k or drop to k−1. There is no single left position where the window becomes valid, so left has nowhere fixed to sit. The brute force that recounts distinct values for every subarray is O(N²) and too slow for 2 × 10⁴.

Turn "exactly" into a subtraction of two "at most"s

Here is the trick that rescues it. Let atMost(x) = the number of subarrays with at most x distinct integers. Then:

Key Insightexactly(k) = atMost(k) − atMost(k−1). Subarrays with at most k distinct, minus those with at most k−1 distinct, leaves precisely those with k − and atMost is a monotonic, windowable quantity.
Counting "at most x" in one pass

Run the at-most-x-distinct window (the Fruit Into Baskets machine with 2 replaced by x). The counting step is the new idea: when the window [left..right] is valid, it contributes right - left + 1 new subarrays — namely every subarray that ends at right, since each start from left to right gives a valid window, and any start before left would exceed x distinct. Summing that over all right counts every at-most-x subarray exactly once.

python
def subarraysWithKDistinct(nums, k):
    def at_most(x):
        count = 0
        left = 0
        freq = {}
        for right in range(len(nums)):
            freq[nums[right]] = freq.get(nums[right], 0) + 1
            while len(freq) > x:
                freq[nums[left]] -= 1
                if freq[nums[left]] == 0:
                    del freq[nums[left]]
                left += 1
            count += right - left + 1     # subarrays ending at right with <= x distinct
        return count
    return at_most(k) - at_most(k - 1)
Crucial Notecount += right - left + 1 is the whole counting insight in one line. It works because within a valid at-most-x window every suffix ending at right is itself valid — so the window's width equals the number of fresh subarrays this step adds.
Worked Example:[1, 2, 1], k=2
Step 1: Calculate AtMost(2)
0
1
1
2
2
1
At index 0, window [1] is valid (distinct <= 2). Adds length 1 to count (subarray: [1]).
0
1
left
1
2
right
2
1
At index 1, window [1, 2] is valid (distinct <= 2). Adds length 2 to count (subarrays: [2], [1, 2]).
0
1
left
1
2
2
1
right
At index 2, window [1, 2, 1] is valid (distinct <= 2). Adds length 3 to count (subarrays: [1], [2, 1], [1, 2, 1]). Total = 1 + 2 + 3 = 6.

Total atMost(2) Subarrays: 6

Step 2: Calculate AtMost(1)
- AtMost(1) = 3 (subarrays: [1], [2], [1]).

Step 3: Subtract
- 6 - 3 = 3 (valid subarrays: [1, 2], [2, 1], [1, 2, 1]).

Interactive Strategy Visualization

K Different Integers

k=2 Exact
Set Difference Trick
1
0
2
1
1
2
2
3
3
4
At Most 2 Count
1
O(N²) Brute Force
O(N) Difference of Two atMost Windows