Given a string s and an integer k, return the length of the longest substring that contains at most k distinct characters.
Input: s = "eceba", k = 2 Output: 3 The longest substring with at most 2 distinct characters is "ece", of length 3.
Input: s = "aa", k = 1 Output: 2 The whole string "aa" has one distinct character.
- 0 <= s.length <= 5 * 10^4 - 0 <= k <= 50 - s consists of lowercase English letters.
This is the general form of the two-basket problem: instead of at most two distinct values, allow at most k. The window and its character-count map are identical — you just compare the map's size against k.
“At most k, or exactly k, distinct?”
At most — fewer than k distinct is fine.
“What if k is 0?”
No characters are allowed, so the answer is 0.
This is the at-most-k-distinct window — the same as fruit baskets but with k instead of two.
I keep a count map of the characters in the window and grow the right edge.
When the map has more than k keys, I shrink from the left until it's back to k, tracking the longest width.
Worked example — s = "eceba", k = 2
right=0 e {e} best 1
right=1 c {e,c} best 2
right=2 e {e,c} best 3 (window "ece")
right=3 b {e,c,b}! shrink -> {c,b}? no, drop e's: -> {b, ...}; window "eb" best 3
right=4 a {b,a} best 3
answer: 3
The number of keys in the count map equals the number of distinct characters in the window — the exact quantity you cap at k.
As you shrink, a character truly leaves the window only when its count reaches zero; deleting it then keeps the map size accurate.
Fruit Into Baskets is this with k = 2. Nothing about the algorithm changes except the threshold you compare the map size against.
| Extend from each start | Sliding window | |
|---|---|---|
| Idea | From each index, extend while <= k distinct | One window, shrink when > k distinct |
| Time | O(n^2) | O(n) |
| Space | O(k) | O(k) |
The brute rebuilds the distinct set from each index; the window carries a count map forward with both pointers moving right only. Full code is in the Approaches selector below.
Key takeaway
Longest substring with at most k distinct characters: grow a window with a character-count map; when the map exceeds k keys, shrink from the left until it's back to k, tracking the longest width. It's Fruit Into Baskets with a parameter — O(n) time, O(k) space.
count = {}; left = 0; best = 0
for right in 0 .. n-1:
count[s[right]] += 1
while len(count) > k: count[s[left]] -= 1; drop if 0; left += 1
best = max(best, right - left + 1)
return best