Longest Substring with at most K Distinct Characters
Given a string skk
Example 1
Input: s = "eceba", k = 2
Output: 3
Explanation: The substring "ece" has 2 distinct characters and length 3.
Output: 3
Explanation: The substring "ece" has 2 distinct characters and length 3.
Example 2
Input: s = "aa", k = 1
Output: 2
Explanation: The substring "aa" has 1 distinct character and length 2.
Output: 2
Explanation: The substring "aa" has 1 distinct character and length 2.
Example 3
Input: s = "aabbcc", k = 2
Output: 4
Explanation: The substrings "aabb" or "bbcc" have 2 distinct characters and length 4.
Output: 4
Explanation: The substrings "aabb" or "bbcc" have 2 distinct characters and length 4.
Constraints:
1 <= s.length <= 5 * 1040 <= k <= 50
Solution - Sliding Window with Frequency Map
This is a classic dynamic sliding window problem. We expand the window by moving
rightHashMapkk
Implementation steps:
-
Use a
to track the frequency of each character in the window.HashMap<Character, Integer> -
Expand the window by adding
to the map.s.charAt(right) -
When
, shrink the window from the left: decrement the count ofmap.size() > k and remove it from the map when its count reaches 0.s.charAt(left) -
After shrinking, update
with the current window size.maxLen
Java
public int lengthOfLongestSubstringKDistinct(String s, int k) {
if (k == 0 || s == null || s.isEmpty()) return 0;
Map<Character, Integer> freq = new HashMap<>(); // char -> frequency in window
int left = 0, maxLen = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
freq.put(c, freq.getOrDefault(c, 0) + 1); // expand window
// Shrink window until we have at most k distinct characters
while (freq.size() > k) {
char leftChar = s.charAt(left);
freq.put(leftChar, freq.get(leftChar) - 1);
if (freq.get(leftChar) == 0) {
freq.remove(leftChar); // distinct count drops by 1
}
left++;
}
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
Trace through Example 1: s = "eceba", k = 2
| right | char | map | left | window | maxLen |
|---|---|---|---|---|---|
| 0 | e | {e:1} | 0 | "e" | 1 |
| 1 | c | {e:1,c:1} | 0 | "ec" | 2 |
| 2 | e | {e:2,c:1} | 0 | "ece" | 3 |
| 3 | b | {e:1,c:1,b:1} → shrink → {e:1,b:1} | 2 | "eb" | 3 |
| 4 | a | {e:1,b:1,a:1} → shrink → {b:1,a:1} | 3 | "ba" | 3 |
Complexity Analysis
-
Time complexity: O(n). Each character is added to the window once by
and removed at most once byright . Total operations: O(2n) = O(n).left -
Space complexity: O(k). The frequency map holds at most
entries at any point (before shrinking).k + 1