Given a string s
, count the number of non-empty substrings that contain only one distinct letter. Each substring must be contiguous and consist of the same repeated character. Return the total count of such substrings.
Constraints:
1 <= s.length <= 1000
s
consists only of lowercase English letters.
Example:
Input: s = "aaaba"
Output: 8
Explanation: The substrings are: "a", "a", "a", "aa", "aa", "aaa", "b", "a".
The problem asks us to find all substrings within s
that consist of only a single (repeated) character. The most straightforward (brute-force) approach would be to generate all possible substrings and count those that meet the requirement. However, this would be inefficient, especially for longer strings, as the number of substrings grows quadratically.
To optimize, we can observe that for every contiguous block of the same character (e.g., "aaa"), all its substrings are valid. For a block of length n
, the count of such substrings is the sum of the first n
natural numbers: n * (n + 1) / 2
. This insight allows us to process the string in a single pass, grouping consecutive identical characters and summing the counts for each group.
We'll solve the problem efficiently by counting contiguous groups of identical characters and summing the number of substrings for each group.
s
(starting from the second character), check if it matches the previous character:
group_length * (group_length + 1) / 2
, add to the total, and reset the group length to 1.This approach ensures we process the string in linear time and count all valid substrings efficiently.
Let's walk through the example s = "aaaba"
:
3 * (3 + 1) / 2 = 6
. Add to total. Reset group length = 1 for 'b'.1 * (1 + 1) / 2 = 1
. Add to total. Reset group length = 1 for 'a'.1 * (1 + 1) / 2 = 1
. Add to total.Thus, the answer is 8, matching the expected output.
Brute-force approach:
The optimized approach is much more efficient, especially for large inputs.
By recognizing that each block of consecutive identical characters can be handled in constant time, we reduce the problem from a quadratic brute-force solution to a simple linear scan. The key insight is using the formula for the number of substrings in a block, which makes the solution both elegant and efficient.
class Solution:
def countLetters(self, s: str) -> int:
total = 0
count = 1
for i in range(1, len(s)):
if s[i] == s[i-1]:
count += 1
else:
total += count * (count + 1) // 2
count = 1
total += count * (count + 1) // 2
return total
class Solution {
public:
int countLetters(string s) {
int total = 0, count = 1;
for (int i = 1; i < s.size(); ++i) {
if (s[i] == s[i-1]) {
count++;
} else {
total += count * (count + 1) / 2;
count = 1;
}
}
total += count * (count + 1) / 2;
return total;
}
};
class Solution {
public int countLetters(String s) {
int total = 0, count = 1;
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == s.charAt(i - 1)) {
count++;
} else {
total += count * (count + 1) / 2;
count = 1;
}
}
total += count * (count + 1) / 2;
return total;
}
}
var countLetters = function(s) {
let total = 0, count = 1;
for (let i = 1; i < s.length; ++i) {
if (s[i] === s[i-1]) {
count++;
} else {
total += count * (count + 1) / 2;
count = 1;
}
}
total += count * (count + 1) / 2;
return total;
};