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;
    }
};
posted @ 2022-12-25 17:30  danieldai  阅读(18)  评论(0编辑  收藏  举报