
|-----------|
| 🚀 算法题 🚀 |
🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀
🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨
🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎
🌲 恭喜你发现一枚宝藏博主,赶快收入囊中吧🌻
🌲 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯
|-----------|
| 🚀 算法题 🚀 |


🍔 目录
-
- [🚩 题目链接](#🚩 题目链接)
- [⛲ 题目描述](#⛲ 题目描述)
- [🌟 求解思路&实现代码&运行结果](#🌟 求解思路&实现代码&运行结果)
-
- [⚡ 滑动窗口](#⚡ 滑动窗口)
-
- [🥦 题目要求](#🥦 题目要求)
- [🥦 核心逻辑拆解](#🥦 核心逻辑拆解)
- [🥦 测试案例分析](#🥦 测试案例分析)
- [🥦 实现代码](#🥦 实现代码)
- [🥦 关键原理说明(为什么 cnt += left 是对的)](#🥦 关键原理说明(为什么 cnt += left 是对的))
- [🥦 运行效率](#🥦 运行效率)
- [🥦 思考](#🥦 思考)
- [💬 共勉](#💬 共勉)
🚩 题目链接
⛲ 题目描述
给你一个字符串 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 。
🌟 求解思路&实现代码&运行结果
⚡ 滑动窗口
🥦 题目要求
- 题目要求统计同时包含 a、b、c 三种字符的所有子串数量。
- 核心技巧:滑动窗口维护区间 left, right,保证窗口不满足同时有 a/b/c,通过 left 的值快速算出以 right 为右端点的合法子串总数。
🥦 核心逻辑拆解
- 右指针 right 逐个遍历字符,不断扩大右边界,记录窗口内 a/b/c 的出现次数num0,num1,num2。
- 只要当前窗口 left,right 三种字符齐全,就不断右移 left、减少左侧字符计数,直到窗口缺少至少一种字符。
- 此时所有起点在0 ~ left-1、终点为 right 的子串,一定都同时包含 a/b/c,合法子串数量正好等于 left,累加到结果 cnt。
- 遍历完所有 right,累加总和就是答案。
🥦 测试案例分析
举个直观例子:s = "abcabc"
- right=2(字符 c):进入 while 循环收缩 left 到 1,cnt += 1
- right=3(字符 a):继续收缩 left到 2,cnt += 2
- right=4(字符 b):收缩 left 到 3,cnt +=3
- right=5(字符 c):收缩 left到 4,cnt +=4
总和 1+2+3+4 = 10,和样例输出一致。
🥦 实现代码
java
class Solution {
public int numberOfSubstrings(String s) {
int n = s.length();
int cnt = 0;
int[] num = new int[3];
int left = 0;
for (int right = 0; right < n; right++) {
num[s.charAt(right) - 'a']++;
while (num[0] > 0 && num[1] > 0 && num[2] > 0) {
num[s.charAt(left++) - 'a']--;
}
cnt += left;
}
return cnt;
}
}
🥦 关键原理说明(为什么 cnt += left 是对的)
循环结束后窗口 left, right 一定不满足三种字符齐全。
那么:
- 起点 0,1,...,left-1,终点 right:子串 s0,right、s1,right...sleft-1,right
- 这些子串都包含完整 a/b/c,总共有 left 个。
🥦 运行效率
- 时间复杂度 O (n):left 和 right 最多各自遍历字符串一次,无嵌套多层循环。
- 空间复杂度 O (1):仅固定长度 3的数组,额外空间不随字符串长度变化。
🥦 思考
求解方法:滑动窗口/双指针
核心考察:利用滑动窗口统计满足字符频次条件的子串数量。
思考:如果字符种类变成 k 种,你的代码需要怎么灵活调整呢?
💬 共勉
|----------------------------------|
| 最后,我想和大家分享一句一直激励我的座右铭,希望可以与大家共勉! |

