【LeetCode-395】至少有K个重复字符的最长子串

问题

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

示例

输入: s = "ababbc", k = 2
输出: 5
解释: 最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。

解答1:滑动窗口

class Solution {
public:
    int longestSubstring(string s, int k) {
        int res = 0;
        for (int i = 1; i < 27; i++) { // 遍历窗口中可能存在的字符种类数量
            int m[26] = {0}; // 统计每个字符出现的次数
            int word_cnt = 0, ok_cnt = 0; // 分别为字符种类数、满足条件的字符种类数
            int left = 0, right = 0;
            while (right < s.size()) {
                m[s[right] - 'a']++;
                if (m[s[right] - 'a'] == 1) word_cnt++;
                if (m[s[right++] - 'a'] == k) ok_cnt++;
                while (word_cnt > i) { // word_cnt大于i时left不可能大于等于right
                    if (m[s[left] - 'a'] == 1) word_cnt--;
                    if (m[s[left] - 'a'] == k) ok_cnt--;
                    m[s[left++] - 'a']--;
                }
                if (word_cnt == ok_cnt) res = max(res, right - left); // 窗口中字符满足要求时,比较长度
            }
        }
        return res;
    }
};

重点思路

本题要求满足某个条件的最大字符串长度,第一时间想到用滑动窗口做。能使用滑动窗口法的题目必须具备一个特点:一定存在一个对窗口的限制,迫使左指针右移。本题的限制为窗口中的字符最小重复次数为k。但是,当前窗口中出现次数小于k的字符,在右指针向右移动的过程中可能又满足这个条件了,所以我们不知道到底什么时候移动左指针。

这个时候,我们从字符串自身的特性开始考虑:字母的种类只有26种。当题目给出的要求不足以限制左指针的移动时,我们人为地给滑动窗口加一个限制:滑动窗口中字符数量不超过ii的取值范围为1~26。因为给滑动窗口加的是另外的限制条件,此时滑动窗口中的字符串不一定满足题目的要求,所以我们在更新结果的时候要额外加上题目的要求。

解答2:分治

class Solution {
public:
    int longestSubstring(string s, int k) {
        int n = s.size(), pos = 0, m[26] = {0};
        if (n < k) return 0; // 递归终止条件1:s长度小于k
        for (char w : s) // 统计s中所有字符出现的次数
            m[w - 'a']++;
        while (pos < n && m[s[pos] - 'a'] >= k) // 满足题目要求的字符
            pos++;
        if (pos == n) return n; // 递归终止条件2:s满足题目要求
        int left = longestSubstring(s.substr(0, pos), k); // 分治左边
        while (pos < n && m[s[pos] - 'a'] < k) // 不满足题目要求的字符,剪枝
            pos++;
        int right = longestSubstring(s.substr(pos), k); // 分治右边
        return max(left, right); // 返回左右的最大值
    }
};

重点思路

分治算法的本质是递归,将字符串从1个位置切开,分别递归左边和右边。上述算法对分治算法进行了剪枝,

posted @ 2021-02-27 15:27  tmpUser  阅读(170)  评论(0编辑  收藏  举报