problemmediumalgorithmsleetcode-159leetcode 159leetcode159

Longest Substring with At Most Two Distinct Characters

MediumUpdated: Aug 1, 2025
Practice on:

Problem

Given a string s, find the length of the longest substring t that contains at most 2 distinct characters.

Examples

Example 1:

Input: "eceba"
Output: 3
Explanation: t is "ece" which its length is 3.

Example 2:

Input: "ccaabbb"
Output: 5
Explanation: t is "aabbb" which its length is 5.

Solution

Method 1 – Sliding Window with Hash Map

Intuition

We want the longest substring with at most two distinct characters. As we scan the string, we can use a sliding window to keep track of a valid substring, and a hash map to count the characters in the window. When the window contains more than two distinct characters, we shrink it from the left until it is valid again.

Approach

  1. Use two pointers (left and right) to represent the window.
  2. Expand right to include new characters, updating their count in a hash map.
  3. If the map has more than two keys, move left forward and decrease counts until only two distinct characters remain.
  4. Track the maximum window size throughout.

Code

C++
#include <unordered_map>
#include <string>
using namespace std;

class Solution {
public:
    int lengthOfLongestSubstringTwoDistinct(string s) {
        unordered_map<char, int> count;
        int left = 0, maxLen = 0;
        for (int right = 0; right < s.size(); ++right) {
            count[s[right]]++;
            while (count.size() > 2) {
                if (--count[s[left]] == 0) count.erase(s[left]);
                ++left;
            }
            maxLen = max(maxLen, right - left + 1);
        }
        return maxLen;
    }
};
Go
func lengthOfLongestSubstringTwoDistinct(s string) int {
    count := make(map[byte]int)
    left, maxLen := 0, 0
    for right := 0; right < len(s); right++ {
        count[s[right]]++
        for len(count) > 2 {
            count[s[left]]--
            if count[s[left]] == 0 {
                delete(count, s[left])
            }
            left++
        }
        if right-left+1 > maxLen {
            maxLen = right - left + 1
        }
    }
    return maxLen
}
Java
import java.util.HashMap;

class Solution {
    public int lengthOfLongestSubstringTwoDistinct(String s) {
        HashMap<Character, Integer> count = new HashMap<>();
        int left = 0, maxLen = 0;
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            count.put(c, count.getOrDefault(c, 0) + 1);
            while (count.size() > 2) {
                char leftChar = s.charAt(left);
                count.put(leftChar, count.get(leftChar) - 1);
                if (count.get(leftChar) == 0) count.remove(leftChar);
                left++;
            }
            maxLen = Math.max(maxLen, right - left + 1);
        }
        return maxLen;
    }
}
Kotlin
class Solution {
    fun lengthOfLongestSubstringTwoDistinct(s: String): Int {
        val count = mutableMapOf<Char, Int>()
        var left = 0
        var maxLen = 0
        for (right in s.indices) {
            count[s[right]] = count.getOrDefault(s[right], 0) + 1
            while (count.size > 2) {
                val leftChar = s[left]
                count[leftChar] = count[leftChar]!! - 1
                if (count[leftChar] == 0) count.remove(leftChar)
                left++
            }
            maxLen = maxOf(maxLen, right - left + 1)
        }
        return maxLen
    }
}
Python
class Solution:
    def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
        count = {}
        left = max_len = 0
        for right, c in enumerate(s):
            count[c] = count.get(c, 0) + 1
            while len(count) > 2:
                left_char = s[left]
                count[left_char] -= 1
                if count[left_char] == 0:
                    del count[left_char]
                left += 1
            max_len = max(max_len, right - left + 1)
        return max_len
Rust
use std::collections::HashMap;

impl Solution {
    pub fn length_of_longest_substring_two_distinct(s: String) -> i32 {
        let s = s.as_bytes();
        let mut count = HashMap::new();
        let (mut left, mut max_len) = (0, 0);
        for right in 0..s.len() {
            *count.entry(s[right]).or_insert(0) += 1;
            while count.len() > 2 {
                let left_char = s[left];
                *count.get_mut(&left_char).unwrap() -= 1;
                if count[&left_char] == 0 {
                    count.remove(&left_char);
                }
                left += 1;
            }
            max_len = max_len.max(right - left + 1);
        }
        max_len as i32
    }
}
TypeScript
function lengthOfLongestSubstringTwoDistinct(s: string): number {
    const count: Record<string, number> = {};
    let left = 0, maxLen = 0;
    for (let right = 0; right < s.length; right++) {
        const c = s[right];
        count[c] = (count[c] || 0) + 1;
        while (Object.keys(count).length > 2) {
            const leftChar = s[left];
            count[leftChar]--;
            if (count[leftChar] === 0) delete count[leftChar];
            left++;
        }
        maxLen = Math.max(maxLen, right - left + 1);
    }
    return maxLen;
}

Complexity

  • Time complexity: O(n), where n is the length of s. Each character is visited at most twice (once by right, once by left).
  • 🧺 Space complexity: O(1), since the map holds at most 3 characters (constant size alphabet).

Comments