📌  相关文章
📜  门| Sudo GATE 2020 Mock II(2019 年 1 月 10 日)|问题 7(1)

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

Sudo GATE 2020 Mock II(2019 年 1 月 10 日)- Question 7

本题是关于字符串的题目。

问题描述

给定一个字符串s和一个整数k,在字符串s中找到最长的连续子字符串,使得该子字符串包含的不同字符数不超过k个。

例如,给定字符串 aabacbebebe 和k=3,则符合条件的最长子字符串为 cbebebe

编写一个Python函数来解决这个问题。

def longest_substring(s: str, k: int) -> str:
    pass
输入
  • s: 字符串s,长度为n(1≤n≤10^4)
  • k: 整数k,k的值在[1,n]范围内
输出

该函数应该返回一个字符串,表示符合条件的最长子字符串。如果有多个字符串符合条件,则返回任意一个即可。

示例

输入:

s = 'aabacbebebe'
k = 3
longest_substring(s, k)

输出:

'cbebebe'
解决方案
思路

由题意可知,要想找到最长的连续子字符串,使得该子字符串包含的不同字符数不超过k个,我们需要使用滑动窗口算法。在此算法中,我们将滑动窗口右侧指向的字符不断添加到窗口中,直到字符数超过了k,此时我们需要向右移动左侧指针,直到字符数重新降低到不超过k。

我们需要使用一个字典来维护当前滑动窗口中每个字符出现的次数,从而快速计算当前滑动窗口中包含的不同字符数。每次右侧指针向右移动时,我们需要将其指向的字符出现次数加1。每次左侧指针向左移动时,我们需要将其指向的字符出现次数减1。在所有这样的移动中,我们需要不断更新最长的子字符串,并在移动完成后返回它。

具体而言,我们从字符串的最左侧开始,通过右侧指针向右扩张窗口,每次都检查字符数是否不超过k。如果字符数不超过k,我们就可以将右侧指针向右移动以增加窗口的大小。如果字符数超过了k,我们就需要移动左侧指针,直到窗口中的字符数重新不超过k。在移动左侧指针时,我们需要将对应字符的出现次数减1,并检查当前窗口是否为最长的符合条件的子字符串。

代码实现
def longest_substring(s: str, k: int) -> str:
    if not s:
        return ""
    
    n = len(s)
    if k >= n:
        return s
    
    left, right = 0, 0
    max_len = 0
    result = ''
    window = {}
    
    while right < n:
        # 添加 right 指向的字符
        if s[right] not in window:
            window[s[right]] = 1
        else:
            window[s[right]] += 1
        
        # 左侧指针移动
        while len(window) > k:
            window[s[left]] -= 1
            if window[s[left]] == 0:
                del window[s[left]]
            left += 1
        
        # 更新最长的子字符串
        if right - left + 1 > max_len:
            max_len = right - left + 1
            result = s[left:right+1]
        
        # 右侧指针移动
        right += 1
    
    return result
复杂度分析
  • 时间复杂度:O(n),其中n是给定字符串的长度。最坏情况下,我们最多只会处理整个字符串一次。
  • 空间复杂度:O(k),k表示的是给定的整数k,即滑动窗口的长度。最坏情况下,整个字符串都没有重复字符,也就是要将所有字符存入字典中,此时唯一的空间限制就是这个字典,它最多包含k个键值对。