395. 至少有 K 个重复字符的最长子串 (滑动窗口) 是真的难,没一天功夫,根部写不了
class Solution {
public:
int longestSubstring(string s, int k) {
int res_left = 0, res_right = 0;
for(int t = 1;t <= 26; t++){//分成26种情况讨论
int left=0,right = 0; // 窗口边界
int lgst_left=0,lgst_right = 0; // 当前情况下的最好窗口边界
int total = 0; // 窗口中已有字符的种数
int less = 0; //出现的次数小于k次的字符的种数
int cnt[26] = {0}; //每种字符出现的次数
while(right < s.size()){ // 窗口右边界没有出界时
while(total <= t && right < s.size()){ // 窗口右边界右移到窗口中含有t种字符为止
if(cnt[s[right]-'a'] == 0){ // 某个字符在窗口中的出现次数是0,是新进到窗口中
if(total != t){ // 并且这个时候窗口还可以新进字符
total++; // 窗口中的字符种数加1
less++; // 少于k次的字符种数也加1
cnt[s[right]-'a']++;
if(cnt[s[right] - 'a'] == k) //如果k==1的话,那么少于k次的字符种数减1
{
less--;
}
}else{ //当窗口不可以新进字符了,窗口右边界结束
break;
}
}else{ //右边的字符是一个已经在窗口中有的字符,更新窗口的属性
cnt[s[right]-'a']++;
if(cnt[s[right] - 'a'] == k) // 一个字符在窗口中出现次数达到k次,少于k次的字符种数减1。
{
less--;
}
}
right++; //窗口右界向右移一位
}
// 判断该窗口子串是否满足每一个字符出现的次数都不少于k,并且记录最长的子串
if(!less&& right - 1 - left > lgst_right - lgst_left) {
lgst_left = left;
lgst_right = right - 1; //right 属于窗口的下一个位置
}
//移动窗口左边界,使尝试找出下一个窗口。
while(total == t){ //窗口中的字符种数是t 就右移,直到窗口中的字符种数不为t.
if(cnt[s[left]-'a'] == k){ // 窗口左界右移时,某一字符数出现次数等于k,那么马上会少于k,所以 少于k次的字符数加1
less++;
}
cnt[s[left]-'a']--;
if(cnt[s[left]-'a']==0){ //窗口左界右移时,某一字符数出现次数等于0,那么少于k次的字符数减1,因为没有这个字符了
less--;
total--;
}
left++; // 窗口左界右移一位。
}
}
if(lgst_left!=lgst_right&& lgst_right - lgst_left > res_right - res_left){ // 如果产生了出现次数都不少于K次的子字符,比较下该子字符串是否比较长,比较长就替换现有的。
res_left = lgst_left;
res_right = lgst_right;
}
}
if(res_right == res_left && k == 1 && s.size() == 1) return 1; //单独情况,字符串只有一个字符,并且出现只有一次的情况
return res_right!=res_left ? res_right - res_left + 1:0;
}
};