📌  相关文章
📜  所有字符至少出现K次的最大子字符串|套装2(1)

📅  最后修改于: 2023-12-03 15:25:44.463000             🧑  作者: Mango

所有字符至少出现K次的最大子字符串|套装2

背景:假设有一个字符串s和一个整数k,找到s中所有字符至少出现k次的最大子字符串。

示例

输入:s = "ababbc", k = 2 输出:5 解释:最大子字符串为 "ababb" ,其中字符 'a' 和 'b' 都至少出现了 2 次。

方法一:暴力搜索

我们可以使用双重循环遍历所有子字符串,对于每个子字符串,检查其中每个字符是否都至少出现k次。时间复杂度为O(n^3),效率较低,无法通过所有测试用例。

方法二:分治法

我们可以使用分治法,将字符串s分成多个小字符串,然后处理每个小字符串来找到最大子字符串。为了保证每个小字符串中字符出现次数不少于k,则需要统计每个字符的出现次数。时间复杂度为O(nlogn)。

方法三:滑动窗口

我们可以使用滑动窗口来减少时间复杂度。首先,我们可以统计字符串s中每个字符出现的次数,并将所有出现次数不少于k的字符放置在一个集合中。接下来,我们可以使用两个指针,一个左指针和一个右指针,来维护一个滑动窗口。然后,我们可以扩大右指针,直到窗口中包含所有集合中的字符,但窗口尾部字符出现的次数少于k。此时,我们将右指针向右移动,同时也移除窗口尾部的字符。然后,我们继续向右移动左指针,直到窗口中不再包含所有集合中的字符。这时候,我们就找到了一个可行的子字符串。重复这个过程,直到找到最大子字符串。时间复杂度为O(n)。

下面是Java的代码实现:

public int longestSubstring(String s, int k) {
    int n = s.length();
    if (n < k) return 0;
    int[] count = new int[26];
    for (char c : s.toCharArray()) count[c - 'a']++;
    Set<Character> set = new HashSet<>();
    for (int i = 0; i < 26; i++) {
        if (count[i] >= k) set.add((char)(i + 'a'));
    }
    if (set.isEmpty()) return 0;
    int res = 0, i = 0;
    while (i + k <= n) {
        boolean flag = true;
        int mask = 0, max_idx = i;
        for (int j = i; j < n; j++) {
            int idx = s.charAt(j) - 'a';
            if (!set.contains(s.charAt(j))) {
                i = j + 1;
                flag = false;
                break;
            }
            if (count[idx] < k) {
                mask |= 1 << idx;
            } else {
                mask &= ~(1 << idx);
            }
            if (mask == 0) {
                res = Math.max(res, j - i + 1);
                max_idx = j;
            }
        }
        if (flag) break;
        i = max_idx + 1;
    }
    return res;
}
方法四:二分查找

我们可以使用二分查找来进一步优化上述滑动窗口解法。首先,我们可以将k从1到s中最多出现次数的字符次数之间进行二分查找,找到最大的k值,使得s中的所有字符都至少出现k次。然后,我们可以使用滑动窗口解法来找到最大子字符串。时间复杂度为O(nlogn)。

下面是Java的代码实现:

public int longestSubstring(String s, int k) {
    int n = s.length();
    if (n < k) return 0;
    int[] count = new int[26];
    for (char c : s.toCharArray()) count[c - 'a']++;
    Set<Character> set = new HashSet<>();
    for (int i = 0; i < 26; i++) {
        if (count[i] >= k) set.add((char)(i + 'a'));
    }
    if (set.isEmpty()) return 0;
    int res = 0;
    for (int t = 1; t <= set.size(); t++) {
        int i = 0, j = 0, unique = 0, noLessThanK = 0;
        int[] cur = new int[26];
        while (j < n) {
            boolean valid = true;
            if (cur[s.charAt(j) - 'a'] == 0) unique++;
            cur[s.charAt(j) - 'a']++;
            if (cur[s.charAt(j) - 'a'] == k) noLessThanK++;
            while (unique > t) {
                if (cur[s.charAt(i) - 'a'] == k) noLessThanK--;
                cur[s.charAt(i) - 'a']--;
                if (cur[s.charAt(i) - 'a'] == 0) unique--;
                i++;
            }
            if (unique == t && noLessThanK == t) {
                res = Math.max(res, j - i + 1);
                valid = false;
            }
            j++;
            if (valid && j == n && t == set.size()) {
                t++;
                j = i + 1;
            }
        }
    }
    return res;
}