寻找最小覆盖子串 - LeetCode 76

寻找最小覆盖子串 - LeetCode 76

LeetCode76. 最小覆盖子串
给定一个字符串 s 和一个字符串 t,我们的任务是找到 s 中包含 t 所有字符的最小子串。如果找不到,返回空字符串。

问题背景

这个问题实际上是滑动窗口(Sliding Window)类型的问题,通常用于在字符串中查找满足一些条件的子串。在该问题中,我们要找到一个包含 t 所有字符的最小子串。

相关知识

在解决这个问题之前,让我们了解一些相关的知识点。

滑动窗口

滑动窗口是一种经典的算法思想,通常用于解决一些字符串和数组相关的问题。它通过维护一个子数组或子串,通过左右边界的移动,来寻找符合某种条件的子数组或子串。

滑动窗口算法的一般步骤如下:

  1. 初始化左右指针 leftright,通常都指向数组的第一个元素。
  2. 移动右指针 right,扩大窗口,直到满足某种条件,停止。
  3. 移动左指针 left,缩小窗口,直到不满足条件,停止。
  4. 重复步骤2和步骤3,直到遍历完整个数组。

哈希表

哈希表(Hash Table)是一种非常常见的数据结构,用于存储键-值对(Key-Value Pairs)。它通过哈希函数将键映射到对应的位置,从而实现了快速的查找和插入操作。

问题介绍

给定字符串 st,我们要找到 s 中包含 t 所有字符的最小子串。如果不存在这样的子串,返回空字符串。

问题示例

让我们通过一些示例来更好地理解这个问题。

示例 1

输入:

s = "ADOBECODEBANC", t = "ABC"

输出:

"BANC"

解释:

最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。

示例 2

输入:

s = "a", t = "a"

输出:

"a"

解释:

整个字符串 s 就是最小覆盖子串。

示例 3

输入:

s = "a", t = "aa"

输出:

""

解释:

t 中有两个字符 ‘a’,但 s 中不存在符合条件的子字符串,因此返回空字符串。

解题思路

为了解决这个问题,我们可以使用滑动窗口算法。我们需要维护两个指针 leftright,分别表示窗口的左边界和右边界,以及一个哈希表 need 来记录字符串 t 中每个字符的出现次数。

具体步骤如下:

  1. 初始化指针 leftright,以及哈希表 need
  2. 从左到右移动右指针 right,扩大窗口,如果 s[right]t 中的字符,更新哈希表 need
  3. 当窗口包含 t 中所有字符时,移动左指针 left,缩小窗口,更新 need 和记录结果。
  4. 重复步骤2和步骤3,直到遍历完整个字符串 s

让我们来看一下代码实现,同时加入注释以更好地理解每一步。

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if len(s) < len(t):
            return ""  # 如果s比t短,无法包含t的所有字符,直接返回空字符串
        
        need = collections.defaultdict(int)  # 创建哈希表用于记录t中每个字符的出现次数
        for c in t:
            need[c] += 1  # 初始化need哈希表
        
        needCnt = len(t)  # 需要的字符总数
        left = 0  # 滑动窗口的左指针
        res = (0, float('inf'))  # 记录结果,初始窗口范围为正无穷大
        
        for right, c in enumerate(s):
            if need[c] > 0:
                needCnt -= 1  # 当s[right]是t中字符,needCnt减少
            need[c] -= 1  # 更新need哈希表
            
            if needCnt == 0:  # 当needCnt为0,表示窗口包含了t的所有字符
                while True:
                    c = s[left]
                    if need[c] == 0:
                        break
                    need[c]

 += 1  # 移动左指针,缩小窗口,同时更新need哈希表
                    left += 1
                
                if right - left < res[1] - res[0]:  # 更新最小窗口范围
                    res = (left, right)
                
                need[s[left]] += 1  # 左指针右移,需要的字符加1
                needCnt += 1  # needCnt加1,窗口不再包含t的所有字符
                left += 1  # 左指针右移
        
        return '' if res[1] > len(s) else s[res[0]:res[1]+1]  # 返回最小覆盖子串

时间和空间复杂度

  • 时间复杂度:O(N),其中 N 为字符串 s 的长度。我们遍历字符串 s 一次,同时使用双指针维护滑动窗口。
  • 空间复杂度:O(K),其中 K 为字符串 t 的长度。哈希表 need 最多包含 t 中所有不同字符的出现次数。

结论

通过使用滑动窗口算法和哈希表,在时间复杂度为 O(N) 的情况下,我们成功找到了字符串 s 中包含字符串 t 所有字符的最小子串。这是一个经典的算法问题,也是一个非常重要的技巧。希望这篇博客对你有所帮助,让你更好地理解和掌握这一技术。

你可能感兴趣的:(leetcode,算法,职场和发展)