Longest Substring with At Most K Distinct Characters
Master this topic with zero to advance depth.
Longest Substring with At Most K Distinct Characters
Given a string s and an integer k, return the length of the longest substring of s that contains at most k distinct characters.
Visual Representation
s = "eceba", k = 2
Indices: 0 1 2 3 4
Chars: e c e b a
1. Window [0,0]: {e} (1 distinct) <= 2, Max=1
2. Window [0,1]: {e, c} (2 distinct) <= 2, Max=2
3. Window [0,2]: {e, c, e} (2 distinct) <= 2, Max=3
4. Window [0,3]: {e, c, e, b} (3 distinct) > 2
- Shrink from left: [1,3] -> {c, e, b} (3 distinct) > 2
- Shrink from left: [2,3] -> {e, b} (2 distinct) <= 2, Max=3Examples
The substring is "ece" which has length 3.
Level I: Brute Force
Intuition
Generate all possible substrings and for each substring, count the number of distinct characters. If it's <= k, update the maximum length.
Detailed Dry Run
Input: s = "eceba", k = 2
i=0:j=0 (e): {e}, size=1 <= 2. maxLen = 1j=1 (c): {e, c}, size=2 <= 2. maxLen = 2j=2 (e): {e, c}, size=2 <= 2. maxLen = 3j=3 (b): {e, c, b}, size=3 > 2. BREAK
i=1:j=1 (c): {c}, size=1 <= 2. maxLen = 3j=2 (e): {c, e}, size=2 <= 2. maxLen = 3j=3 (b): {c, e, b}, size=3 > 2. BREAK
⚠️ Common Pitfalls & Tips
O(N^2) time complexity is too slow for N=10^5.
Level II: Sliding Window (Using Dictionary/Frequency Map)
Intuition
Instead of checking all substrings, use two pointers to maintain a window. Expand the right pointer, and if the count of unique characters exceeds k, move the left pointer until the count is back to k.
Detailed Dry Run
Input: s = "eceba", k = 2
r=0 (e): map={e:1}, size=1. max=1r=1 (c): map={e:1, c:1}, size=2. max=2r=2 (e): map={e:2, c:1}, size=2. max=3r=3 (b): map={e:2, c:1, b:1}, size=3 > 2.- Move
l=0 (e): map={e:1, c:1, b:1}, size=3 - Move
l=1 (c): map={e:1, b:1}, size=2. loop ends.
- Move
⚠️ Common Pitfalls & Tips
O(N) time but O(K) space for the mapping.
Level III: Optimal (Sliding Window + Hash Map)
Intuition
Use a sliding window with two pointers i and j. Maintain a frequency map of characters in the current window. If the number of distinct characters exceeds k, shrink the window from the left until it's valid again.
Add characters from the right. When map.size() > k, remove from the left and update the count. Keep track of the max j - i + 1.
Detailed Dry Run
eceba, k=2. Window: [e,c,e] -> ok, max=3. Add 'b': [e,c,e,b] -> distinct=3 > 2. Shrink left until distinct <= 2: [e,b] -> ok, len=2.
⚠️ Common Pitfalls & Tips
Ensure the character count reaches zero before removing it from the map. Using a map size as the distinct count is robust.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.