寻找最小覆盖子串 - LeetCode 76
LeetCode76. 最小覆盖子串
给定一个字符串 s
和一个字符串 t
,我们的任务是找到 s
中包含 t
所有字符的最小子串。如果找不到,返回空字符串。
问题背景
这个问题实际上是滑动窗口(Sliding Window)类型的问题,通常用于在字符串中查找满足一些条件的子串。在该问题中,我们要找到一个包含 t
所有字符的最小子串。
相关知识
在解决这个问题之前,让我们了解一些相关的知识点。
滑动窗口
滑动窗口是一种经典的算法思想,通常用于解决一些字符串和数组相关的问题。它通过维护一个子数组或子串,通过左右边界的移动,来寻找符合某种条件的子数组或子串。
滑动窗口算法的一般步骤如下:
- 初始化左右指针
left
和right
,通常都指向数组的第一个元素。 - 移动右指针
right
,扩大窗口,直到满足某种条件,停止。 - 移动左指针
left
,缩小窗口,直到不满足条件,停止。 - 重复步骤2和步骤3,直到遍历完整个数组。
哈希表
哈希表(Hash Table)是一种非常常见的数据结构,用于存储键-值对(Key-Value Pairs)。它通过哈希函数将键映射到对应的位置,从而实现了快速的查找和插入操作。
问题介绍
给定字符串 s
和 t
,我们要找到 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
中不存在符合条件的子字符串,因此返回空字符串。
解题思路
为了解决这个问题,我们可以使用滑动窗口算法。我们需要维护两个指针 left
和 right
,分别表示窗口的左边界和右边界,以及一个哈希表 need
来记录字符串 t
中每个字符的出现次数。
具体步骤如下:
- 初始化指针
left
和right
,以及哈希表need
。 - 从左到右移动右指针
right
,扩大窗口,如果s[right]
是t
中的字符,更新哈希表need
。 - 当窗口包含
t
中所有字符时,移动左指针left
,缩小窗口,更新need
和记录结果。 - 重复步骤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
所有字符的最小子串。这是一个经典的算法问题,也是一个非常重要的技巧。希望这篇博客对你有所帮助,让你更好地理解和掌握这一技术。