Problem Statement
You are given a string s and an integer k. Your task is to find the length of the longest substring in s that contains at most k distinct characters.
Examples
Example 1
Input: s = “eceba", K = 2
Output: 3
Explanation:
The longest substring with at most 2 distinct characters is "ece".
Different Approaches
1 Brute Force Approach
Algorithm:
- Initialize maxLen to 0.
- Iterate i from 0 to n - 1, where n is the length of the string s.
- Iterate j from i to n - 1.
- Consider the substring s[i...j].
- Count the number of distinct characters in the substring by inserting in set.
- If the count of distinct characters is less than or equal to k, update maxLen with the maximum length of the substring.
- Iterate j from i to n - 1.
- Return maxLen.
Code Implementation in C++:
#include <iostream>
#include <unordered_set>
using namespace std;
int longestSubstringWithAtMostKDistinctChars(string s, int k) {
int n = s.size();
int maxLen = 0;
// Iterate through all possible substrings
for (int i = 0; i < n; i++) {
unordered_set<char> uniqueChars;
for (int j = i; j < n; j++) {
uniqueChars.insert(s[j]);
if (uniqueChars.size() <= k) {
maxLen = max(maxLen, j - i + 1);
}
}
}
// Return the maximum length of the substring
return maxLen;
}
int main() {
string s1 = "eceba";
int k1 = 2;
cout << "Longest substring with at most " << k1 << " distinct characters in \"" << s1 << "\": ";
cout << longestSubstringWithAtMostKDistinctChars(s1, k1) << endl;
string s2 = "aa";
int k2 = 1;
cout << "Longest substring with at most " << k2 << " distinct characters in \"" << s2 << "\": ";
cout << longestSubstringWithAtMostKDistinctChars(s2, k2) << endl;
return 0;
}
OR with map
#include <iostream>
#include <unordered_map>
using namespace std;
int longestSubstringWithAtMostKDistinctChars(string s, int k) {
int n = s.size();
int maxLen = 0;
// Iterate through all possible substrings
for (int i = 0; i < n; i++) {
unordered_map<char, int> mp; // Hashmap to store the frequency of characters in the substring
for (int j = i; j < n; j++) {
mp[s[j]]++; // Increase the frequency of the current character
if (mp.size() <= k) { // If the count of distinct characters is less than or equal to k
maxLen = max(maxLen, j - i + 1); // Update maxLen with the maximum length of the substring
}
}
}
// Return the maximum length of the substring
return maxLen;
}
int main() {
string s1 = "eceba";
int k1 = 2;
cout << "Longest substring with at most " << k1 << " distinct characters in \"" << s1 << "\": ";
cout << longestSubstringWithAtMostKDistinctChars(s1, k1) << endl;
string s2 = "aa";
int k2 = 1;
cout << "Longest substring with at most " << k2 << " distinct characters in \"" << s2 << "\": ";
cout << longestSubstringWithAtMostKDistinctChars(s2, k2) << endl;
return 0;
}
Complexity Analysis:
Time Complexity:O(n^2)
Space Complexity:O(1)
2 Sliding Window with Map
Algorithm:
- Initialize two pointers left and right to 0.
- Initialize a hashmap mp to store the frequency of characters in the window.
- Initialize a variable maxLen to 0.
- Iterate through the string:
- Update the frequency of the current character in mp.
- If the number of distinct characters in the current window exceeds k, move the left pointer to minimize the window.
- Update maxLen with the maximum length of the window.
- Return maxLen.
Example:
Consider the string s = “eceba”
and k = 2
Initialization:
- left = 0, right = 0
- maxLen = 0
- mp = {} (empty hashmap)
- distinctChars = 0
s = "eceba"
k = 2
left = 0, right = 0
maxLen = 0
mp = {}
distinctChars = 0
Iteration 1:
- Consider character
e
. - Increment the frequency of
e
inmp
. - Since
mp['e'] == 1
, incrementdistinctChars
. - Update
maxLen
tomax(0, 0 - 0 + 1) = 1
.
s = "eceba"
k = 2
left = 0, right = 0
maxLen = 1
mp = {'e': 1}
distinctChars = 1
Iteration 2:
- Consider character c.
- Increment the frequency of c in mp.
- Since mp['c'] == 1, increment distinctChars.
- Update maxLen to max(1, 1 - 0 + 1) = 2.
s = "eceba"
k = 2
left = 0, right = 1
maxLen = 2
mp = {'e': 1, 'c': 1}
distinctChars = 2
Iteration 3:
- Consider character e.
- Increment the frequency of e in mp.
- Update maxLen to max(2, 2 - 0 + 1) = 3.
s = "eceba"
k = 2
left = 0, right = 2
maxLen = 3
mp = {'e': 2, 'c': 1}
distinctChars = 2
Iteration 4:
- Consider character b.
- Increment the frequency of b in mp.
- Since distinctChars > k, move the left pointer to minimize the window.
- Decrease the frequency of e in mp.
- Since mp['e'] > 0, decrement distinctChars.
s = "eceba"
k = 2
left = 1, right = 3
maxLen = 3
mp = {'e': 1, 'c': 1, 'b': 1}
distinctChars = 2
Iteration 5:
- Consider character a.
- Increment the frequency of a in mp.
- Since distinctChars > k, move the left pointer to minimize the window.
- Decrease the frequency of c in mp.
- Since mp['c'] > 0, decrement distinctChars.
s = "eceba"
k = 2
left = 2, right = 4
maxLen = 3
mp = {'e': 1, 'c': 0, 'b': 1, 'a': 1}
distinctChars = 2
Final Result:
The longest substring with at most 2 distinct characters is "ece", with a length of 3.
Code Implementation in C++:
#include <iostream>
#include <unordered_map>
using namespace std;
int longestSubstringWithAtMostKDistinctChars(string s, int k) {
unordered_map<char, int> mp;
int n = s.size();
int maxLen = 0;
int distinctChars = 0;
int left = 0, right = 0;
// Sliding window approach
while (right < n) {
// Increase the frequency of the current character
mp[s[right]]++;
// If the frequency becomes 1, increment the count of distinct characters
if (mp[s[right]] == 1) {
distinctChars++;
}
// If the number of distinct characters exceeds k, move the left pointer
while (distinctChars > k) {
// Decrease the frequency of the leftmost character
mp[s[left]]--;
// If the frequency becomes 0, decrement the count of distinct characters
if (mp[s[left]] == 0) {
distinctChars--;
}
// Move the left pointer to minimize the window
left++;
}
// Update maxLen with the maximum length of the window
maxLen = max(maxLen, right - left + 1);
// Move the right pointer to expand the window
right++;
}
// Return the maximum length of the window
return maxLen;
}
int main() {
string s1 = "eceba";
int k1 = 2;
cout << "Longest substring with at most " << k1 << " distinct characters in \"" << s1 << "\": ";
cout << longestSubstringWithAtMostKDistinctChars(s1, k1) << endl;
string s2 = "aa";
int k2 = 1;
cout << "Longest substring with at most " << k2 << " distinct characters in \"" << s2 << "\": ";
cout << longestSubstringWithAtMostKDistinctChars(s2, k2) << endl;
return 0;
}
OR
int lengthOfLongestSubstringKDistinct(string s, int k) {
unordered_map<char, int> cnt;
int n = s.size();
int ans = 0, j = 0;
for (int i = 0; i < n; ++i) {
cnt[s[i]]++;
while (cnt.size() > k) {
if (--cnt[s[j]] == 0) {
cnt.erase(s[j]);
}
++j;
}
ans = max(ans, i - j + 1);
}
return ans;
}
Complexity Analysis:
Time Complexity: O(n)
- The time complexity of the sliding window approach is O(N), where N is the length of the input string s.
- In each iteration, both the left and right pointers move at most N steps, and the frequency of characters is updated in constant time.
Space Complexity:O(k)
- The space complexity is
O(k)
, wherek
is the number of distinct characters allowed in the window. - The space required for the hashmap mp is proportional to the number of distinct characters.