Longest Substring with At Most K Distinct Characters
Fast recall
Quick revision
Core answer
HashMap of character frequencies. Expand right and add characters; when the map has more than K distinct keys, shrink from the left, decrementing counts and removing a key when it hits zero. Track the maximum window length. This runs in O(n) time.Whether you can adapt the sliding window template to a distinct-character constraint and correctly manage map key removal
Forgetting to remove the key from the map when its count drops to zero, causing incorrect distinct-character counts
Call out the map-key-removal edge case explicitly and explain why each character is processed at most twice
Interview-ready guide
Interview answer
To find the longest substring with at most K distinct characters, I'd use a variable-size sliding window with a HashMap.
I have two pointers, left and right, both starting at zero. The HashMap tracks the frequency of each character in the current window. The number of keys in the map tells me how many distinct characters I have.
I move the right pointer forward through the string, adding each character to the map and incrementing its count. After each addition, I check if the map size is greater than K. If it is, the window is invalid because I have too many distinct characters.
I enter a while loop that shrinks the window from the left. In each iteration, I get the character at the left pointer, decrement its frequency in the map, and then check if the frequency is now zero. If it is, I must remove that key from the map entirely.
This is crucial because if I leave a key with count zero in the map, the map size will be wrong. After removing zero-count keys, I increment the left pointer. This continues until the map size is less than or equal to K again.
Once the window is valid, I calculate its size as right minus left plus one and update my maximum length if this window is larger. The time complexity is O of n because each character enters the map once and leaves at most once, even though there's a while loop inside the for loop. The key mistake to avoid is forgetting to remove the map key when its count drops to zero.
For example, in araaci with k = 2, the window grows to araa, which has only a and r and length 4. Adding c creates three distinct characters, so the left side shrinks until the window is valid again.
Longest Substring with At Most K Distinct Characters
Direct Answer
Use a variable-size sliding window with a HashMap of character frequencies. Expand right and add characters; when the map has more than K distinct keys, shrink from the left, decrementing counts and removing a key when it hits zero. Track the maximum window length. This runs in O(n) time.
Deep Dive & Architecture
Use a variable-size window and a frequency map. Expand the right boundary for every character. When the map contains more than k distinct characters, move the left boundary forward, decreasing counts and removing a character when its count reaches zero.
Worked example: araaci, k = 2
The window grows through a, ar, ara, and araa; it contains only a and r, so its best length becomes 4. Adding c creates three distinct values. Remove characters from the left until only two remain: removing a twice does not remove it from the map, but removing r finally leaves a and c. The window is valid again. Adding i triggers another shrink.
The invariant is that the window is valid whenever its distinct count is at most k. Update the best length only after restoring that condition. Each character enters once and leaves at most once, so time is O(n), and the map uses O(k) space while valid.
For k = 0, the answer is normally 0. If the requirement says exactly k distinct characters rather than at most k, track the same windows but accept a candidate only when the map size equals k.
Code Example
import java.util.HashMap;
import java.util.Map;
public class LongestSubstringKDistinct {
public static int lengthOfLongestSubstringKDistinct(String s, int k) {
if (k == 0 || s.isEmpty()) return 0;
Map<Character, Integer> freq = new HashMap<>();
int left = 0, maxLength = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
freq.put(c, freq.getOrDefault(c, 0) + 1);
while (freq.size() > k) { // too many distinct -> shrink
char l = s.charAt(left);
freq.put(l, freq.get(l) - 1);
if (freq.get(l) == 0) freq.remove(l); // key removal keeps size correct
left++;
}
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
public static void main(String[] args) {
System.out.println(lengthOfLongestSubstringKDistinct("eceba", 2)); // 3 ("ece")
}
}
Common Mistakes & Pitfalls
- Not removing zero-count keys: decrementing to zero but leaving the key makes
map.size()overcount distinct characters — remove the key when it hits zero. - Misreading map.size(): it's the number of keys (distinct characters), not the sum of frequencies.
- Shrinking too aggressively: the while condition is
map.size() > k, not>= k, or you shrink valid windows.
Check your understanding
Answer these without looking back at the article.
- What is the sliding window technique and when do you use it?
- How do you find the maximum sum subarray of size k?
- How do you find the longest substring without repeating characters?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.