给定两个字符串 s 和 t,长度分别是 m 和 n,返回 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 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
m == s.lengthn == t.length1 <= m, n <=s和t由英文字母组成
解法思路
- 统计目标字符频率 :用字典
need记录字符串t中每个字符的出现次数。 - 滑动窗口遍历 :用左右指针维护滑动窗口,右指针先扩大窗口,直到窗口包含
t的所有字符;再移动左指针缩小窗口,寻找最小满足条件的子串。 - 验证窗口有效性 :用变量
valid记录窗口中满足need要求的字符种类数,当valid等于need的长度时,窗口满足条件。 - 记录最小窗口:在窗口满足条件时,持续更新最小窗口的起始位置和长度。
Python 代码:
python
from collections import defaultdict
class Solution:
def minWindow(self, s: str, t: str) -> str:
"""
寻找字符串s中包含字符串t所有字符的最小覆盖子串
:param s: 源字符串
:param t: 目标字符串(需要包含的所有字符)
:return: 满足条件的最小子串,无则返回空字符串
"""
# 边界条件1:目标字符串t为空,直接返回空
if not t:
return ""
# 边界条件2:源字符串s比t短,不可能包含,返回空
if len(s) < len(t):
return ""
# 统计t中各字符的需求次数(key:字符,value:需要的次数)
need = defaultdict(int)
for c in t:
need[c] += 1
# 滑动窗口内的字符统计(记录窗口中各字符的出现次数)
window = defaultdict(int)
left = right = valid = 0 # valid:窗口中满足"次数达标"的字符种类数
start = 0 # 最小窗口的起始索引(最终结果的起始位置)
min_len = float('inf') # 最小窗口的长度(初始化为无穷大)
# 右指针遍历,扩大窗口
while right < len(s):
# 取出当前右指针指向的字符,然后右指针右移
current_char = s[right]
right += 1
# 若当前字符是t需要的,更新窗口内的统计
if current_char in need:
window[current_char] += 1
# 当窗口内该字符的数量 == 需求数量时,该字符达标,valid+1
if window[current_char] == need[current_char]:
valid += 1
# 当窗口满足所有字符需求(valid等于需要的字符种类数),尝试缩小左窗口
while valid == len(need):
# 更新最小窗口的信息(如果当前窗口更短)
current_window_len = right - left
if current_window_len < min_len:
start = left # 记录最小窗口的起始位置
min_len = current_window_len # 更新最小长度
# 取出左指针指向的字符,准备移出窗口
remove_char = s[left]
left += 1 # 左指针右移,缩小窗口
# 若移出的字符是t需要的,更新窗口统计和valid
if remove_char in need:
# 若移出前该字符刚好达标,移出后不再达标,valid-1
if window[remove_char] == need[remove_char]:
valid -= 1
# 窗口内该字符的计数-1
window[remove_char] -= 1
# 最终判断:若min_len仍为无穷大,说明无满足条件的窗口,否则返回子串
return "" if min_len == float('inf') else s[start:start + min_len]
# ------------------- 测试用例 -------------------
if __name__ == "__main__":
solution = Solution()
# 测试用例1:基础场景
s1 = "ADOBECODEBANC"
t1 = "ABC"
print(f"测试用例1结果:{solution.minWindow(s1, t1)}") # 预期输出:"BANC"
# 测试用例2:t包含重复字符
s2 = "a"
t2 = "a"
print(f"测试用例2结果:{solution.minWindow(s2, t2)}") # 预期输出:"a"
# 测试用例3:无满足条件的子串
s3 = "a"
t3 = "aa"
print(f"测试用例3结果:{solution.minWindow(s3, t3)}") # 预期输出:""
# 测试用例4:t为空
s4 = "abc"
t4 = ""
print(f"测试用例4结果:{solution.minWindow(s4, t4)}") # 预期输出:""
# 测试用例5:s和t完全一致
s5 = "abcdef"
t5 = "abcdef"
print(f"测试用例5结果:{solution.minWindow(s5, t5)}") # 预期输出:"abcdef"
LeetCode提交代码:
python
class Solution:
def minWindow(self, s: str, t: str) -> str:
from collections import defaultdict
# 统计t中各字符的需求次数
need = defaultdict(int)
for c in t:
need[c] += 1
# 滑动窗口内的字符统计
window = defaultdict(int)
left = right = valid = 0 # valid:满足需求的字符种类数
start = 0 # 最小窗口的起始索引
min_len = float('inf') # 最小窗口的长度
while right < len(s):
# 右指针右移,扩大窗口
c = s[right]
right += 1
# 若当前字符是t需要的,更新窗口统计
if c in need:
window[c] += 1
# 当窗口内该字符的数量满足需求时,valid+1
if window[c] == need[c]:
valid += 1
# 当窗口满足所有字符需求时,尝试左移窗口缩小范围
while valid == len(need):
# 更新最小窗口的信息
current_len = right - left
if current_len < min_len:
start = left
min_len = current_len
# 左指针右移,缩小窗口
d = s[left]
left += 1
# 若移出的字符是t需要的,更新窗口统计和valid
if d in need:
# 若移出前该字符数量刚好满足需求,移出后不再满足,valid-1
if window[d] == need[d]:
valid -= 1
window[d] -= 1
# 若未找到满足条件的窗口,返回空字符串;否则返回最小子串
return "" if min_len == float('inf') else s[start:start+min_len]
程序运行截图展示:

总结
题目要求从字符串s中找出包含字符串t所有字符(包括重复字符)的最短子串。采用滑动窗口算法,通过双指针动态调整窗口范围:右指针扩展窗口直至满足条件,左指针收缩窗口以寻找最小解。使用哈希表统计字符频率,并通过valid变量验证窗口有效性。若存在满足条件的子串,返回最小窗口对应的子串;否则返回空字符串。时间复杂度为O(m+n),适用于英文字符串场景。测试用例验证了算法的正确性,包括基础场景、重复字符及无解情况。