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

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

所有字符至少出现 K 次的最大子串
问题描述

对于一个字符串s和一个整数k,找到s中所有字符至少出现k次的最大子串。

解决方案

方法一:暴力枚举

对于该问题,一个暴力的方法就是枚举所有的子串并判断其中是否所有的字符都出现了k次。但是,这种方法的时间复杂度为O(n^3),显然不能接受。

方法二:滑动窗口

我们考虑用一个滑动窗口,并用一个哈希表来记录当前子串中每个字符出现的次数。如果某个字符出现的次数少于k次,那么该字符肯定不在最大子串中。我们可以通过移动左右指针来更新哈希表,并将当前子串中的所有字符都出现k次的子串与当前最大子串进行比较。

def maxSubString(s:str, k:int) -> int:
    n = len(s)
    left, right = 0, 0
    max_len = 0
    hash_map = {}
    while right < n:
        hash_map[s[right]] = hash_map.get(s[right], 0) + 1
        right += 1
        while left < right and min(hash_map.values()) < k:
            hash_map[s[left]] -= 1
            if hash_map[s[left]] == 0:
                del hash_map[s[left]]
            left += 1
        if len(hash_map) == 26:
            max_len = max(max_len, right - left)
    return max_len

方法三:分治法

我们可以将原问题转化为子问题:求解所有字符至少出现k次的最大子串,要么存在于左半部分,要么存在于右半部分,要么跨越中点。对于前两种情况,可以使用递归解决。针对第三种情况,需要求出跨越中点的最大子串。我们可以用一个哈希表来记录当前子串中每个字符出现的次数,并用两个指针left和right从中点出发,分别向左右扩展子串。记录左侧满足条件的最长子串和右侧满足条件的最长子串,两者长度之和即为跨越中点的最长子串长度。

def getMaxSubstring(s:str, k:int, start:int, end:int) -> int:
    if end - start + 1 < k:
        return 0
    hash_map = {}
    for i in range(start, end + 1):
        hash_map[s[i]] = hash_map.get(s[i], 0) + 1
    while end - start + 1 >= k and hash_map[s[start]] < k:
        start += 1
    while end - start + 1 >= k and hash_map[s[end]] < k:
        end -= 1
    if end - start + 1 < k:
        return 0
    for i in range(start, end + 1):
        if hash_map[s[i]] < k:
            return max(getMaxSubstring(s, k, start, i - 1), getMaxSubstring(s, k, i + 1, end))
    return end - start + 1

def maxSubString(s:str, k:int) -> int:
    return getMaxSubstring(s, k, 0, len(s) - 1)
结论

三种方法中,滑动窗口方法是最优解,时间复杂度为O(n)。分治法虽然时间复杂度也为O(n),但需要递归处理,常数较大。暴力枚举的时间复杂度为O(n^3),且无法处理特别长的字符串。