Problem

Given a string, find the longest substring that contains only k unique or distinct characters.

Examples

Example 1:

Input: S = "aabacbebebe", K = 3
Output: 7
Explanation: "cbebebe" is the longest substring with 3 distinct characters.

Example 2:

Input: 
S = "aaaa", K = 2
Output: -1
Explanation: There's no substring with 2 distinct characters.

Solutions

Method 1 - Brute Force

If the length of string is n, then there can be n*(n+1)/2 possible substrings. A simple way is to generate all the substring and check each one whether it has exactly k unique characters or not. If we apply this brute force, it would take O(n^2) to generate all substrings and O(n) to do a check on each one. Thus overall it would go O(n^3).

We can further improve this solution by creating a hash table and while generating the substrings, check the number of unique characters using that hash table. Thus it would improve up to O(n^2).

Method 2 - Sliding Window with Frequency Hashmap

This problem is based on the variable-size sliding window pattern. We have already solved a similar problem Find Smallest Subarray with a given sum K greater than or equal using this pattern. We will use the same strategy to solve this problem as well.

  • Start with a sliding window of size 1 (left=0, right=0). We expand window with right, and shrink it by moving left
  • Use a map to store frequency for each character in the current window.
  • Iterate over the string and insert new characters in the HashMap until we have exactly K distinct characters in the HashMap.
    • Iterate through the string, expanding the window by adding characters until we have exactly K distinct characters in the HashMap (representing the current longest substring with K unique characters).
    • If the window contains more than K distinct characters, shrink it from the left by removing the leftmost element from the HashMap, continuing until we have K or fewer distinct characters.

Code

Java
class Solution {

	private int lengthOfLongestSubstringWithKUniqChars(String s, int k) {
		int n = s.length();
		// answer - length of longest substring with k uniq chars
		int ans = -1;
		// frequency map of chars in window
		Map<Character, Integer> windowCharCnt = new HashMap<>();
		int left = 0; // left = start of window, right end of window
		for (int right = 0; right < n; right++) {
			char c = s.charAt(right);
			
			windowCharCnt.put(c, windowCharCnt.getOrDefault(c, 0) + 1);

			// Shrink the sliding window
			// until we have exactly 'k' distinct characters in the window
			while (windowCharCnt.size() > k) {
				char leftChar = s.charAt(left);

				windowCharCnt.put(leftChar, windowCharCnt.get(leftChar) - 1);

				if (windowCharCnt.get(leftChar) == 0) {
					windowCharCnt.remove(leftChar);
				}

				left++;
			}

			if (windowCharCnt.size() == k) {
				ans = Math.max(ans, right - left + 1);
			}
		}

		return ans;
	}
}

Complexity

  • ⏰ Time complexity: O(n)
  • 🧺 Space complexity: O(n)