给你一个字符串 s
,它只包含三种字符 a, b 和 c 。
请你返回 a,b 和 c 都 至少 出现过一次的子字符串数目。
示例 1:
输入:s = "abcabc" 输出:10 解释:包含 a,b 和 c 各至少一次的子字符串为 "abc", "abca", "abcab", "abcabc", "bca", "bcab", "bcabc", "cab", "cabc" 和 "abc" (相同字符串算多次)。
示例 2:
输入:s = "aaacb" 输出:3 解释:包含 a,b 和 c 各至少一次的子字符串为 "aaacb", "aacb" 和 "acb" 。
示例 3:
输入:s = "abc" 输出:1
提示:
3 <= s.length <= 5 x 10^4
s
只包含字符 a,b 和 c 。
提示 1
For each position we simply need to find the first occurrence of a/b/c on or after this position.
提示 2
So we can pre-compute three link-list of indices of each a, b, and c.
解法1:提示1 + 提示2
固定右边界 i ,找到能满足要求的(a,b 和 c 都 至少 出现过一次)最大左边界 start ,
则此时满足要求的子字符串为:[start,... ,i] [start - 1,..., i] ... [0,..., i]
此时子字符串的数量为start + 1。
class Solution {
public int numberOfSubstrings(String s) {
int n = s.length();
List<Integer> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
List<Integer> c = new ArrayList<>();
for (int i = 0; i < n; i++) {
switch (s.charAt(i)) {
case 'a':
a.add(i);
break;
case 'b':
b.add(i);
break;
case 'c':
c.add(i);
break;
}
}
int ans = 0;
for (int i = n - 1; i >= 2; i--) {
if (a.size() == 0 || b.size() == 0 || c.size() == 0) {
break;
}
int start = Math.min(Math.min(a.get(a.size() - 1), b.get(b.size() - 1)), c.get(c.size() - 1));
ans += start + 1;
while (a.size() > 0 && a.get(a.size() - 1) >= i) {
a.remove(a.size() - 1);
}
while (b.size() > 0 && b.get(b.size() - 1) >= i) {
b.remove(b.size() - 1);
}
while (c.size() > 0 && c.get(c.size() - 1) >= i) {
c.remove(c.size() - 1);
}
}
return ans;
}
}
复杂度分析
- 时间复杂度:O(n),n 是 字符串s 的长度。
- 空间复杂度:O(n)。
解法2:不定长滑动窗口
固定左边界left ,找到能满足要求的(a,b 和 c 都 至少 出现过一次)最小右边界 right ,
然后收缩左边界,找到能满足要求的最大左边界 left。
则此时满足要求的子字符串为:[left,... , right] [left - 1,..., right] ... [0,..., right]
此时子字符串的数量为 left + 1。
class Solution {
public int numberOfSubstrings(String s) {
int n = s.length();
int ans = 0;
int[] window = new int[3];
int left = 0;
int right = 0;
while (right <= n - 1 && left <= n - 3) {
switch (s.charAt(right) - 'a') {
case 0:
window[0]++;
break;
case 1:
window[1]++;
break;
case 2:
window[2]++;
break;
}
if (window[0] >= 1 && window[1] >= 1 && window[2] >= 1) {
while (window[s.charAt(left) - 'a'] > 1) {
window[s.charAt(left) - 'a']--;
left++;
}
ans += left + 1;
}
right++;
}
return ans;
}
}
复杂度分析
- 时间复杂度:O(n),n 是 字符串s 的长度。
- 空间复杂度:O(1)。