旺崽的博客

要么天赋异禀,要么天道酬勤

0%

LeetCode 395 至少有 K 个重复字符的最长子串

题目链接

给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

示例 1:

1
2
3
输入:s = "aaabb", k = 3
输出:3
解释:最长子串为 "aaa" ,其中 'a' 重复了 3 次。

示例 2:

1
2
3
输入:s = "ababbc", k = 2
输出:5
解释:最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。

双指针~
这题的关键在于怎么利用双指针找答案,我一开始判断的条件就是两根指针划定的区间的子串每个字符的出现次数,后来发现它并不满足临界点的性质,比如示例2,当子串为 $abab$ 时是满足的,$ababb$ 也是满足的,所以我们无法判断其中一个指针是否到达了临界点~
对此我们可以再限制一个条件,就是字符的种类数 $i$,当前子串的种类数大于 $i$ 时,证明已达临界点,我们就可以移动另一个指针了,中间的判断一样,我们可以遍历每一种字符的个数,判断是否满足条件;为了节省时间空间,我们可以开一个 $less$ 变量记录字符的种类数,当某一字符的出现次数达到 $k$ 时,就减 $1$,这样当 $less=0$ 时,一定满足该子串所有字符的出现次数大于等于 $k$~
AC代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
int longestSubstring(string s, int k) {
int ans = 0;
int n = s.length();
for (int i = 1; i <= 26; i++) {
int l = 0, r = 0;
vector<int> cnt(26, 0);
int tot = 0, less = 0;
while (r < n) {
cnt[s[r] - 'a']++;
if (cnt[s[r] - 'a'] == 1) {
tot++;
less++;
}
if (cnt[s[r] - 'a'] == k) less--;
while (tot > i) {
cnt[s[l] - 'a']--;
if (cnt[s[l] - 'a'] == k - 1) less++;
if (cnt[s[l] - 'a'] == 0) tot--, less--;
l++;
}
if (!less) ans = max(ans, r - l + 1);
r++;
}
}
return ans;
}
};