找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。
示例 1:
输入:
s = "aaabb", k = 3
输出:
3
最长子串为 "aaa" ,其中 'a' 重复了 3 次。
示例 2:
输入:
s = "ababbc", k = 2
输出:
5
最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。
1.emmm...,其实我没搞懂这个思路。。。
class Solution {
public:
int longestSubstring(string s, int k) {
int res = 0, i = 0, n = s.size();
while (i + k <= n) {
int m[26] = {0}, mask = 0, max_idx = i;
for (int j = i; j < n; ++j) {
int t = s[j] - 'a';
++m[t];
if (m[t] < k) mask |= (1 << t);
else mask &= (~(1 << t));
if (mask == 0) {
res = max(res, j - i + 1);
max_idx = j;
}
}
i = max_idx + 1;
}
return res;
}
};
2.注:以下需要注意的是 nums[s[start] - 'a'] < k,中间之前忘记s。。。,并且对于&&、||等符号两侧最好加括号,尤其是不肯定符号优先级顺序的时候。
while ((end - start + 1 >= k) && (nums[s[start] - 'a'] < k)) start++;
while ((end - start + 1 >= k) && (nums[s[end] - 'a'] < k)) end--;
还要注意:这里i的范围为[start,end]!!
for (int i = start; i <= end; ++i){
nums[s[i] - 'a']++;
}
//分治:
//设置这样的递归函数: 统计当前范围内的字符频数, 然后先左后右截断多余的出现频数小于k的字符, 若截断至字符长度小于k则直接返回0
// 否则: 对中间任意位置进行判断, 如果中间某个字符出现频数小于k, 则截断它, return 左右子串的结果最大值
// 如果中间字符频数都大于等于k, 则返回当前字符串长度
class Solution {
private:
int recu(const string& s, int start, int end, int k)
{
if (end - start + 1 < k) return 0;
//统计频数
vector nums(26, 0);
for (int i = start; i <= end; ++i)
{
nums[s[i] - 'a']++;
}
//截断
while ((end - start + 1 >= k) && (nums[s[start] - 'a'] < k)) start++;
while ((end - start + 1 >= k) && (nums[s[end] - 'a'] < k)) end--;
if (end - start + 1 < k) return 0;
//中间判断
for (int i = start + 1; i < end; ++i)
{
if (nums[s[i] - 'a'] < k) return max(recu(s, start, i - 1, k), recu(s, i + 1, end, k));
}
return end - start + 1;
}
public:
int longestSubstring(string s, int k) {
if ((k == 0) || (s.size() < k)) return 0;
else return recu(s, 0, s.size() - 1, k);
}
};