📌  相关文章
📜  每个字符至少出现 k 次的最长子序列(1)

📅  最后修改于: 2023-12-03 14:55:55.534000             🧑  作者: Mango

每个字符至少出现 k 次的最长子序列

给定一个字符串和一个整数 k,找出该字符串中每个字符至少出现 k 次的最长子序列。

解法

由于题目要求每个字符至少出现 k 次,那么我们可以把出现次数小于 k 次的字符排除掉,这样可以减小计算量,在最终得到的子序列中这些字符也没有意义。

接着,我们可以考虑分治法,将问题分解成左右两个部分,分别求解左半部分和右半部分的最长子序列,然后将左右两个部分的最长子序列合并成一个整体的最长子序列。

合并时,需要考虑左半部分的最长子序列中包含的字符对于右半部分也可能有用处,而这些字符在右半部分的最长子序列中却被排除掉了。所以,我们需要将左半部分的最长子序列中包含的字符记录下来,然后在右半部分中查找这些字符,如果能够找到,并且它们在右半部分中出现的次数不少于 k 次,那么将它们加入到整体的最长子序列中。

具体实现可以借助一个哈希表来记录每个字符出现的次数,以及一个位图来记录每个字符是否在左半部分的最长子序列中出现过。

时间复杂度为 O(n log n),其中 n 为字符串长度。

代码实现
def longest_substring(s: str, k: int) -> int:
    if len(s) < k:
        return 0
    
    cnt = [0] * 26
    for c in s:
        cnt[ord(c) - ord('a')] += 1
    
    invalid = set()
    for i in range(26):
        if cnt[i] > 0 and cnt[i] < k:
            invalid.add(chr(i + ord('a')))
    
    if not invalid:
        return len(s)
    
    left, right = 0, 0
    res = 0
    while right < len(s):
        if s[right] in invalid:
            res = max(res, longest_substring(s[left:right], k))
            left = right + 1
        right += 1
    
    if left < len(s) and s[left] not in invalid:
        res = max(res, longest_substring(s[left:], k))
    return res

其中,cnt 数组用来统计每个字符出现的次数,并根据出现次数判断哪些字符需要被排除掉。invalid 集合记录了需要被排除掉的字符。在 while 循环中,如果遇到了一个被排除掉的字符,就需要将左半部分的最长子序列求解出来,并更新整体的最长子序列。最后一个 if 语句是为了处理特殊情况,即最后一个字符不属于排除掉的集合,并且也符合出现次数大于等于 k 的要求。