Skip to content

Sliding Window — Longest Substring with At Most K Distinct Characters ​

↗ LeetCode: Longest Substring with At Most K Distinct Characters · Medium · pattern chapter →

Asked atMetaGoogleAmazonLinkedIn

Given a string s and integer k, return the length of the longest substring containing at most k distinct characters.

Example 1 — s = "eceba", k = 2 → 3 (window "ece") Example 2 — s = "aa", k = 1 → 2Example 3 — s = "a", k = 0 → 0

Constraints — 1 ≤ n ≤ 5 · 10⁴; 0 ≤ k ≤ 50. Brute expands every window in O(n²·k) = 10¹⁰·k ops at n=10⁵ (TLE). Sliding window with a freq map is O(n) = 10⁵ ops.

Progressive Hints
Try to solve it yourself first. Reveal hints only when stuck — each hint is progressively more specific.
---
⏱️Interview timer00:00
Practice under interview pressure. Start when you begin reading; stop when you finish. Save best only when you had a full working solution.
Target for interview: Medium = 20-25 min · Hard = 30-40 min · Best time persists on this device only.

Approach 1 — Try every substring ​

Intuition. For each s[i..j], count distinct characters; track the longest window with ≤ k distinct.

java
int lengthOfLongestSubstringKDistinctBrute(String s, int k) {
    int n = s.length(), best = 0;
    for (int i = 0; i < n; i++) {
        Set<Character> distinct = new HashSet<>();
        for (int j = i; j < n; j++) {
            distinct.add(s.charAt(j));
            if (distinct.size() <= k) best = Math.max(best, j - i + 1);
            else break;
        }
    }
    return best;
}

Complexity — Time O(n²); Space O(σ).


Approach 2 — Sliding window with distinct counter ​

Insight from brute. Growing right never decreases distinct. Once distinct > k, we shrink left — decrementing each char's count; if a count hits 0, distinct drops.

java
int lengthOfLongestSubstringKDistinct(String s, int k) {
    if (k == 0) return 0;
    int[] cnt = new int[128];
    int left = 0, distinct = 0, best = 0;
    for (int right = 0; right < s.length(); right++) {
        if (cnt[s.charAt(right)]++ == 0) distinct++;
        while (distinct > k)
            if (--cnt[s.charAt(left++)] == 0) distinct--;
        best = Math.max(best, right - left + 1);
    }
    return best;
}
Execution Trace
Sliding — s='eceba', k=2
leftrighte0c1e2b3a4cnt = {e:1}distinct = 1best = 1
Step 1 / 5e

Complexity — Time O(n) — each char enters and leaves once; Space O(σ) (≤ 128).


Approach 3 — Ordered map for k large (interview polish) ​

Insight from sliding. If k is very large (10⁴+), tracking distinct explicitly is still O(n), but managing a LinkedHashMap of last-seen indices can express the same logic in a tighter algorithm — remove the least-recently-used char and advance left past it in O(1).

java
int lengthOfLongestSubstringKDistinctLRU(String s, int k) {
    LinkedHashMap<Character, Integer> lastIdx = new LinkedHashMap<>(k, 0.75f, false);
    int left = 0, best = 0;
    for (int right = 0; right < s.length(); right++) {
        char c = s.charAt(right);
        lastIdx.remove(c);
        lastIdx.put(c, right);
        if (lastIdx.size() > k) {
            Map.Entry<Character, Integer> oldest = lastIdx.entrySet().iterator().next();
            left = oldest.getValue() + 1;
            lastIdx.remove(oldest.getKey());
        }
        best = Math.max(best, right - left + 1);
    }
    return best;
}

Complexity — Time O(n); Space O(k).


Try it yourself ​

Complexity summary ​

ApproachTimeSpaceInterview grade
Every substringO(n²)O(σ)baseline
Sliding windowO(n)O(σ)expected optimum
LRU-style ordered mapO(n)O(k)polish

When to use which ​

✨AI Study CompanionBeta
Was this page helpful?

Released under MIT license.

💡 Quick shortcuts
H HintsR Run JavaA AI Help