0%

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

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

对于一个字符串中计数长度<k的字符,他必定为分界,反之,当一个字符串中没有计数<k的字符,他就一定是符合条件的子串。

递归分治
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int longestSubstring(string s, int k) {
return helper(s, 0, s.size() - 1, k);
}
private:
int helper(string& s, int lo, int hi, int k)
{
if(lo > hi)
return 0;
int memo[26]{ 0 };
for(int i = lo; i <= hi; ++i)
++memo[s[i] - 'a'];
for(int i = lo; i <= hi; ++i)
if(memo[s[i] - 'a'] < k)
return max(helper(s, lo, i - 1, k), helper(s, i + 1, hi, k));
return hi - lo + 1;
}
};

以上的方法调用的递归次数较多,实际上一个分割字符可以把字符串分成好多串。就可以减少递归调用的次数

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
30
31
32
33
34
35
36
37
38
class Solution {
public:
int longestSubstring(string s, int k) {
if(k == 1)
return s.size();
return helper(s, 0, s.size() - 1, k);
}
private:
int helper(string& s, int lo, int hi, int k)
{
if(lo > hi)
return 0;
int memo[26]{ 0 };
bool split = false;
for(int i = lo; i <= hi; ++i)
++memo[s[i] - 'a'];
for(int i = lo; i <= hi; ++i)
if(memo[s[i] - 'a'] < k)
{
split = true;
break;
}
if(!split)
return hi - lo + 1;
int i = lo, start = lo;
int ret = 0;
while(i <= hi + 1)
{
if(i == hi + 1 || memo[s[i] - 'a'] < k)
{
ret = max(ret, helper(s, start, i - 1, k));
start = i + 1;
}
++i;
}
return ret;
}
};
滑动窗口

对于每种子串的字符数进行枚举,如果当前字符数大于这个值,就移动左边界

维护一个less变量表示当前子串中还有几个个数未达到k。

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
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
int longestSubstring(string s, int k) {
int n = s.size();
if(k == 1)
return n;
int ret = 0;
for(int i = 1; i <= 26; ++i)
{
int memo[26]{ 0 };
int total = 0;
int less = 0;
int begin = 0, end = 0;
while(end < n)
{
if(++memo[s[end] - 'a'] == 1)
{
++total;
++less;
}
if(memo[s[end] - 'a'] == k)
--less;
while(total > i)
{
if(--memo[s[begin] - 'a'] == 0)
{
--total;
--less;
}
if(memo[s[begin] - 'a'] == k - 1)
++less;
++begin;
}
if(less == 0)
ret = max(ret, end - begin + 1);
++end;
}
}
return ret;
}
};