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.
- 1 <= nums.length <= 2 × 10⁴
- 1 <= nums[i], k <= nums.length
nums = [1,2,1,2,3], k = 27nums = [1,2,1,3,4], k = 33nums = [1,1,1,1], k = 110Count 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.
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⁴.
Here is the trick that rescues it. Let atMost(x) = the number of subarrays with at most x distinct integers. Then:
atMost is a monotonic, windowable quantity.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.
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)count += 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.The window itself is unchanged from Fruit Into Baskets — same at-most-x-distinct machine, slots (a) invalid = more than x distinct, (b)/(c) tally in and out with delete-at-zero. What is new sits around it: the count += width trick to total subarrays, and the atMost(k) − atMost(k−1) reduction to handle an "exactly" condition a window cannot chase directly. Bank both — "count subarrays with exactly k of something" almost always means: count at-most-k, count at-most-(k−1), subtract; and "number of valid subarrays ending here" is the window width. The same recipe converts "exactly k odd numbers", "exactly k distinct", and similar counting variants into two runs of a monotone window.
K Different Integers
The Subtraction Logic
Counting "Exactly K" directly with a window is difficult. Instead, we compute AtMost(K) and AtMost(K-1) and subtract them.
Step-by-Step Logic
Step 1: Calculating 'At Most 2'. Window [1]. 1 distinct element. Subarrays ending here: [1] (count: 1).