一、题目
给你一个字符串 s
和一个整数 k
,请你找出 s
中的最长子串, 要求该子串中的每一字符出现次数都不少于 k
。返回这一子串的长度。
示例 1:
输入:s = "aaabb", k = 3 输出:3 解释:最长子串为 "aaa" ,其中 'a' 重复了 3 次。
示例 2:
输入:s = "ababbc", k = 2 输出:5 解释:最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。
提示:
1 <= s.length <= 104
s
仅由小写英文字母组成1 <= k <= 105
二、代码
class Solution {
public int longestSubstring(String str, int k) {
// 转为字符数组
char[] s = str.toCharArray();
// 记录符合题目要求的最长子串长度
int max = 0;
// 枚举窗口内所有的字符种类数
for (int kinds = 1; kinds <= 26; kinds++) {
// 当前这里一轮窗口内a~z出现的次数
int[] count = new int[26];
// 窗口右边界,每一轮都从头开始
int r = -1;
// 目前窗口内收集了几种字符了
int nowKinds = 0;
// 目前窗口内出现次数>=k次的字符,满足了几种
int satisfyKinds = 0;
// l要尝试每一个位置作为窗口的最左边界
for (int l = 0; l < s.length; l++) { // 右边界不回退
// 窗口右扩的条件:
// 1) 窗口内字符种数不足
// 2)某个字符出现不足K次
// 循环执行条件:r右移一位不能导致越界 并且如果右移一位新加入窗口一个全新的字符,超过了我们要求的kinds个种数了,那也不可以右扩
while (r < s.length - 1 && !(nowKinds == kinds && count[s[r + 1] - 'a'] == 0)) { // 下一个词频为0,就是新出现的,算进来就超了
// 窗口右扩
r++;
// 如果新加入窗口的字符是第一次进入窗口,则将窗口内的字符种数++
if (count[s[r] - 'a'] == 0) {
nowKinds++;
}
// 如果新加入窗口的字符已经在窗口内出现了k-1次了,那么又加入一个就变成了出现k次。则将满足窗口内出现次数>=k次的字符种数++
if (count[s[r] - 'a'] == k - 1) {
satisfyKinds++;
}
// 将新加入的字符出现次数在当前窗口的词频表中++
count[s[r] - 'a']++;
}
// 如果当前满足窗口内出现次数>=k次的字符种数 等于 本轮枚举要求的窗口内字符种数
if (satisfyKinds == kinds) {
// 尝试更新符合题目要求的最长子串长度
max = Math.max(max, r - l + 1);
}
// 窗口左边界右移,弹出一个字符
count[s[l] - 'a']--;
// 如果弹出字符后,这个字符在窗口内就没有了,就将窗口内的字符种数--
if (count[s[l] - 'a'] == 0) {
nowKinds--;
}
// 如果弹出字符后,这个字符在窗口内出现次数不是k次了,变成了k-1次,就将窗口内满足窗口内出现次数>=k次的字符种数--
if (count[s[l] - 'a'] == k - 1) {
satisfyKinds--;
}
}
}
// 返回答案
return max;
}
}
三、解题思路
因为只有a~z小写字母,所以我们可以直接把所有字母的情况都枚举一遍,最多也就只有26个而已。
我在整个字符串中寻找:
- 子串必须只包含一种字符,出现次数大于等于k次,满足这个条件的子串长度有多少,
- 子串必须只包含两种字符,但每一种的出现次数都大于等于k次,满足这个条件的子串长度有多少,
- 子串必须只包含三种字符,但每一种的出现次数都大于等于k次,满足这个条件的子串长度有多少,
- ......
一直枚举到26种,在所有答案中求最大就是答案。
流程:
做窗口固定,例如我们就要求窗口中只有3种字符,如果你迟迟没有到达3种,你就右扩。
那当你扩到再往下一个就出现第4种了,停止扩充
然后看满足的种类数和要求的种类数是不是一样的, 如果不一样就是无效的
R往右移的逻辑:
- 种数不足
- 某个字符出现不足K次
接下来L++,出去一个字符,相应的调整count和种类。再去从新的左边界开始,看能不能找到一个新的满足窗口内只有3中字符并且都满足出现次数条件的子串。整个流程下来,就能找到只有3个字符并且符合条件的子串的最大长度了。
下面就接着去枚举窗口内有4种字符的情况。直到完成26个小写字母的全部枚举,从所有答案中选出最大值就是答案。