Longest Substring with At Least K Repeating Characters

喜欢而已 提交于 2020-02-07 09:12:15

 1. 解析

题目大意,求解最长的子序列,该子序列中不同字符出现的次数不少于k次。

 2. 分析

按照我们正常的思路就是无非就是检测每个字符字串的组合,但存在一个问题是如果如何避免每次从开头的下一个字符开始遍历。就是设置一个mask掩码,字符串只由26个小写字母组成,最多用一个26位的二进制掩码即可表示,另外用一个计数器,记录每个字符出现的次数,若在子串当中的不同字符出现的次数都大于或等于k,那么掩码就会被重置为0,若该子串的长度大于之前的最大长度,用该子串更新当前的最大值。并记录下当前的索引,这是关键,下一次就可以从该位置的下一个位置开始遍历,因为之前的状态已经检测过,防止每次都从头开始遍历。时间复杂度为

例如:s = "ababbc"   k = 2

'a', mask = 1    res = 0   max_index = 0     

'b', mask = 11  res = 0   max_index = 0   

'a', mask = 10  res = 0   max_index = 0    即若字串中的某个字符出现的次数大于或等于k,对应的mask位置就会被置为0

'b', mask = 00  res = 4   max_index = 3

'b', mask = 00  res = 5   max_index = 4

'c', mask = 100  res = 5   max_index = 4

class Solution {
public:
    int longestSubstring(string s, int k) {
        int res = 0;
        int i = 0, max_index = 0;
        int n = s.length();
        
        while (i < n){
            vector<int> count(26, 0);
            int mask = 0;  //mask掩码
            max_index = i; //关键
            for (int j = i; j < n; ++j){
                int t = s[j] - 'a';
                count[t]++;
                if (count[t] < k){ //字符掩码,可以标记出现过的字符
                    mask |= (1 << t);
                }
                else{
                    mask &= (~(1 << t));
                }
                if (!mask){ //若当前字串中不同字符出现的次数均大于或等于k,更新最大值
                    res = max(res, j - i + 1);
                    max_index = j;
                }
            }
            i = max_index + 1;
        }
        
        return res;
    }
};

3. 滑动窗口

参考@Grandyang的思路,由于字符串只由26个小写字母组成,即字串当中最多也就只有26个不同的字符,我们就可以针对窗口内只能出现的不同字符个数进行筛选。i和start分别表示滑动窗口的左边界和右边界,用数组charCnt记录不同字符出现的次数,unique表示不同的字符个数,若当前窗口内出现的不同字符超过规定的,就缩小窗口的范围,将左边界往右移动,最后检测窗口内的不同字符出现的次数是否都大于或等于k,若满足,更新最大值;时间复杂度为O(n),因为第一层是个常数,不影响时间复杂度

class Solution {
public:
    
    int longestSubstring(string s, int k) {
        int res = 0, n = s.length();
        for (int cnt = 1; cnt <= 26; ++cnt){ //窗口内只有可能出现26个不同的字符
            int start = 0, i = 0, unique = 0;
            vector<int> charCnt(26, 0);
            while (i < n){
                bool valid = true;
                if (charCnt[s[i++]-'a']++ == 0) ++unique; //记录字符出现的次数和不同字符的个数
                while (unique > cnt){ //若当前窗口内的不同字符超过规定的cnt,则要缩小窗口
                    if (--charCnt[s[start++]-'a'] == 0)
                        --unique;
                }
                for (int c_cnt : charCnt){ //检查窗口内不同字符出现的个数是否大于或等于k
                    if (0 < c_cnt && c_cnt < k){
                        valid = false;
                        break;
                    }
                }
                if (valid) res = max(res, i - start);    
            }
        }
        
        return res;
    }
};

 [1]https://www.cnblogs.com/grandyang/p/5852352.html 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!