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

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

残疾人康复训练

题目:
给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

解题尝试

最开始的时候一头雾水(因为我是残疾人)
求子串的问题,在我看来,最难的地方就是时间复杂度
因为随着字符串长度的增加,全部字串的数量是呈指数级别上升的,因此所有基于枚举所有字串的暴力算法必定会超时

因为太久没有写这种题目,用的又是陌生的C++,我决定看一下评论区

在看完评论区后,我写出了第一版的代码

class Solution
{
    
    
    public:
        int longestSubstring(string s, int k)
        {
    
    
            /*
            方法一:使用分治的方法
            求最长子串,即求每一个满足条件的子串的长度,然后返回最大的子串
            其中,每一个子串又可以当作主串进行分解,直至分解出空串,结束
            */
            // 若为空串,返回
            if (s.length() == 0)
                return 0;
            // 接下来记录当前字符串的字符出现的次数
            vector<int> count(26, 0);  // 26个小写字母
            for (int i = 0; i < s.length(); i ++)
                count[s[i] - 'a'] ++;
            // 然后判断当前的字符串是否为满足条件的字符串
            int pos = 0;
            while (pos < s.length() && count[s[pos] - 'a'] >= k)
                pos ++;
            // pos == 0,说明整个字符串没有一个字符出现次数大于等于k
            // pos == s.length,说明完全满足条件
            if (pos == s.length() || pos == 0)
                return pos;
            // 若并不是满足条件的字符串,但是有一部分字符出现次数满足条件
            // 说明有可能它的子串能满足条件
            // 将整个字符串按照pos为分界线拆分成两部分
            int left = longestSubstring(s.substr(0, pos), k);
            int right = longestSubstring(s.substr(pos), k);
            return max(left, right);
        }
};

整个算法思想最大的错误在于这里:

// pos == 0,说明整个字符串没有一个字符出现次数大于等于k
// pos == s.length,说明完全满足条件
if (pos == s.length() || pos == 0)
        return pos;

即使pos==0,整个字符串也不一定完全不满足条件
原谅我,我只是一个残疾人,这是我的第一次康复训练,难免会写沙比代码

经过修改和五六次本地debug后,得出的版本如下:

class Solution
{
    
    
public:
    int longestSubstring(string s, int k)
    {
    
    
        /*
        使用分治的方法进行求解
        一个字符串有这几种情况:
        1. 所有字符出现的次数都小于k,返回0,即不存在这种子串
        2. 所有字符出现的次数都大于等于k,返回字符串的长度,即最长子串为自身
        3. 一部分字符出现次数大于等于k,另一部分出现的次数小于k,即有可能存在这种子串
        第三种情况需要对字符串进行拆分处理,关键的难点在于如何对字符串进行拆分
        */
        // 第一步,对字符串的所有字符出现的次数进行统计, 时间复杂度:O(n)
        vector<int> count(26, 0);  // 26个小写英文字母
        for (char c : s)
            count[c - 'a'] ++;
        // 第二步,判断这个字符串属于哪一种情况,时间复杂度:O(1)
        bool allSatisfy = true, allNotSatisfy = true;
        for (auto item : count)
            if (item >= k)
                allNotSatisfy = false;
            else if (item != 0 && item < k)
                allSatisfy = false;
        if (allNotSatisfy)
            return 0;
        else if (allSatisfy)
            return s.length();
        // 第三步,对该字符串进行拆分处理,时间复杂度最大为O(n)
        int pos = 0;
        while (pos < s.length() && count[s[pos] - 'a'] < k)
            pos ++;
        int start = pos;  // 忽略开头那些不符合条件的字符--含有不符合条件的字符的子串必定不可能满足
        while (pos < s.length() && count[s[pos] - 'a'] >= k)
            pos ++;
        int left = longestSubstring(s.substr(start, pos), k);
        while (pos < s.length() && count[s[pos] - 'a'] < k)
            pos ++;
        int right = longestSubstring(s.substr(pos), k);
        return max(left, right);
    }
};

运行时间0ms

思路

首先对这个问题进行分析,给我一个字符串,我能得到的结果只有三种:

  1. 完全不符合条件
  2. 全部符合条件
  3. 一半符合一半不符合

完全符合和完全不符合很容易判断:完全符合就返回当前的字符串的长度,完全不符合就返回0
这里的关键是一半符合一半不符合要怎么进行处理,要怎么样才能把它分成完全符合和完全不符合的情况

对于第三种情况的字符串,其内的字符必定存在一部分是满足条件的,另一部分是不满足条件的
对于任何一个字符串,只要存在不满足条件的字符,那么它一定不是完全满足条件的
所以,我们要以不满足条件的字符为分隔符,寻找最长的子串的长度
例如

"bbaaacbd" 3

这个测试样例,其中c和d是不满足条件的字符,我们将其用#代替,则是求这两个子串

"bbaaa"
"b"

的最长字串的长度
其中,第二个子串明显不符合
第一个子串中的b变成了不满足条件的字符,那么我们将b作为分隔符,再次进行分割,只剩下aaa这个子串,而这个子串符合第二种情况,因此返回其长度,即3

实际操作的时候,受我懒惰的限制,将整个字符串分成左右两部分,分别求其最大长度

分享别人的做法

  1. python之不用脑子法
class Solution(object):
    def longestSubstring(self, s, k):
        if not s:
            return 0
        for c in set(s):
            if s.count(c) < k:
                return max(self.longestSubstring(t, k) for t in s.split(c))
        return len(s)

果然python够直接,适合我这类残疾人使用
但是我还是不喜欢python,除非什么时候它能跑得和C语言一样快

  1. C++之我是抄它的法
class Solution {
    
    
public:
    int longestSubstring(string s, int k) {
    
    
        if (k <= 1) return s.size();
        if (s.empty() || s.size() < k) return 0;
        
        vector<int> hash(128, 0);
        for (char c : s) ++hash[c];
        
        int i = 0;
        while (i < s.size() && hash[s[i]] >= k) ++i;
        if (i == s.size()) return s.size();

        int l = longestSubstring(s.substr(0, i), k);
        while (i < s.size() && hash[s[i]] < k) ++i;
        int r = longestSubstring(s.substr(i), k);
        
        return max(l, r);
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_45206746/article/details/114199160