395. 至少有K个重复字符的最长子串
给你一个字符串 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 <= 10^4
s
仅由小写英文字母组成1 <= k <= 10^5
方法一:枚举 + 滑动窗口
解题思路
- 因为
s
只包含小写字母,我们可以枚举最长子串的字母种类数目,即[1, 26]
。 - 每次枚举,定义滑动窗口
[left, rifht)
。在窗口内我们需要维护:- 每种字母出现次数
counts
; - 总的字母种类数目
total
; - 出现次数少于 k 次的字母种类数目
less
;
- 每种字母出现次数
- 当窗口内字母种类数目超过当前枚举的数量
i
,则需要移动left
指针。 - 计算
total == i && less == 0
时的最大值作为结果。
参考代码
public int longestSubstring(String s, int k) {
// 特判
int n = s.length();
if (k == 1) {
return n;
}
if (k > n) {
return 0;
}
int ret = 0;
// 枚举“最长子串”包含的字母种类数目
for (int i = 1; i <= 26; i++) {
int[] counts = new int[26];
int less = 0, total = 0;
int left = 0, right = 0;
while (right < n) {
int r_num = ++counts[s.charAt(right) - 'a'];
if (r_num == 1) {
total++;
less++;
}
if (r_num == k) {
less--;
}
right++;
while (total > i) {
int l_num = counts[s.charAt(left) - 'a']--;
if (l_num == 1) {
total--;
less--;
}
if (l_num == k) {
less++;
}
left++;
}
if (total == i && less == 0) {
ret = Math.max(ret, right - left);
}
}
}
return ret;
}