You are given a string s
consisting only of the characters 'a'
, 'b'
, and 'c'
. Your task is to count the number of substrings of s
that contain at least one occurrence of each of the three characters.
'a'
, one 'b'
, and one 'c'
.s
.
Constraints:
3 <= s.length <= 5 * 10^4
s
contains only 'a'
, 'b'
, and 'c'
.
The most straightforward way to solve this problem is to consider every possible substring of s
and check if it contains all three characters. However, this brute-force approach would involve checking O(n2) substrings, and each check could take up to O(n) time, which would be too slow for large strings.
To optimize, we need a way to efficiently find, for each starting position, the earliest ending position where all three characters are present. If we can do this, we can count how many valid substrings start at each position without enumerating them all.
This leads to the idea of using a sliding window: we maintain a window (defined by two pointers) that expands until it contains all three characters, then we move the left pointer to look for new windows. This method allows us to efficiently count the valid substrings.
We use the sliding window (two-pointer) technique with a character count map to keep track of the number of 'a'
, 'b'
, and 'c'
in the current window.
left
and right
, both starting at 0. Use a hash map or array to keep count of each character in the current window.
right
pointer to the right, adding each character to the count.
left
and end at any index from right
to the end of the string. This is because adding more characters to the right will still include all three characters.
len(s) - right
to the answer (since every substring ending at or after right
is valid).
left
pointer to the right, decreasing the count of the character at left
, and repeat the process.
This approach ensures that each character is processed at most twice (once when right
moves forward, once when left
moves forward), resulting in an O(n) solution.
Consider the input s = "abcabc"
.
left = 0
, right = 0
, and an empty count map.
right
to 0, 1, 2, adding 'a'
, 'b'
, 'c'
. Now the window [0,2] contains all three characters.
len(s) - right = 6 - 2 = 4
such substrings ("abc"
, "abca"
, "abcab"
, "abcabc"
).
left
to 1, shrink the window, and repeat. Each time the window contains all three characters, count the valid substrings.
Continue this process for each starting position. The total count is the answer.
left
, once by right
). O(1) space, as we only store counts for three characters.
The key insight is to use a sliding window to efficiently find valid substrings containing all three required characters. Instead of checking every possible substring, we count how many valid substrings start at each position by expanding and shrinking the window. This reduces the complexity from O(n3) to O(n), making it feasible for large inputs. The solution elegantly leverages the properties of substrings and the limited character set.
class Solution:
def numberOfSubstrings(self, s: str) -> int:
count = {'a': 0, 'b': 0, 'c': 0}
res = 0
left = 0
for right in range(len(s)):
count[s[right]] += 1
while all(count[char] > 0 for char in 'abc'):
res += len(s) - right
count[s[left]] -= 1
left += 1
return res
class Solution {
public:
int numberOfSubstrings(string s) {
int count[3] = {0, 0, 0};
int res = 0, left = 0;
for (int right = 0; right < s.size(); ++right) {
count[s[right] - 'a']++;
while (count[0] > 0 && count[1] > 0 && count[2] > 0) {
res += s.size() - right;
count[s[left] - 'a']--;
left++;
}
}
return res;
}
};
class Solution {
public int numberOfSubstrings(String s) {
int[] count = new int[3];
int res = 0, left = 0;
for (int right = 0; right < s.length(); right++) {
count[s.charAt(right) - 'a']++;
while (count[0] > 0 && count[1] > 0 && count[2] > 0) {
res += s.length() - right;
count[s.charAt(left) - 'a']--;
left++;
}
}
return res;
}
}
var numberOfSubstrings = function(s) {
let count = {a:0, b:0, c:0};
let res = 0, left = 0;
for (let right = 0; right < s.length; right++) {
count[s[right]]++;
while (count['a'] > 0 && count['b'] > 0 && count['c'] > 0) {
res += s.length - right;
count[s[left]]--;
left++;
}
}
return res;
};