ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

滑动窗口算法解析:LeetCode最小覆盖子串问题

滑动窗口算法解析:LeetCode最小覆盖子串问题

1. 问题背景与核心挑战

这道来自LeetCode的Hard题目要求我们在字符串S中找到包含字符串T所有字符的最短连续子串。举个例子,如果S="ADOBECODEBANC",T="ABC",那么正确结果应该是"BANC",因为它同时包含A、B、C三个字母且长度最短。

这个问题的难点在于如何高效处理两个字符串的字符匹配关系。暴力解法需要O(n²)时间复杂度——遍历所有可能的子串组合,然后逐个检查是否包含T的所有字符。但在实际面试或竞赛中,我们需要更聪明的解法。

2. 滑动窗口算法解析

2.1 基础滑动窗口实现

滑动窗口(Sliding Window)是解决这类子串问题的经典方法。基本思路是维护一个可伸缩的窗口,通过左右指针的移动来寻找最优解:

def minWindow(s: str, t: str) -> str: from collections import defaultdict need = defaultdict(int) for c in t: need[c] += 1 need_cnt = len(t) res = (0, float('inf')) left = 0 for right, c in enumerate(s): if need[c] > 0: need_cnt -= 1 need[c] -= 1 if need_cnt == 0: while True: c = s[left] if need[c] == 0: break need[c] += 1 left += 1 if right - left < res[1] - res[0]: res = (left, right) need[s[left]] += 1 need_cnt += 1 left += 1 return s[res[0]:res[1]+1] if res[1] != float('inf') else ""

这个实现的时间复杂度确实是O(n),因为每个字符最多被左右指针各访问一次。空间复杂度是O(m),其中m是字符集大小。

2.2 关键优化点解析

  1. 哈希表的使用:我们用need字典记录T中每个字符的需求量,正数表示还需要多少个,负数表示窗口中该字符有多余。

  2. need_cnt计数器:这个变量记录当前还缺少多少个字符才能满足要求。当它归零时,说明窗口已包含T的所有字符。

  3. 左指针收缩:当窗口满足条件时,我们尝试左移左指针,直到窗口刚好不满足条件为止,这样可以找到当前右指针位置下的最短有效子串。

3. 算法正确性证明

要证明这个算法的正确性,我们需要确认两点:

  1. 不会错过最优解:当右指针移动到某个位置时,左指针会收缩到尽可能靠右的位置,确保记录的是当前最短有效子串。

  2. 时间复杂度保证:每个字符最多被左右指针各访问一次,没有嵌套循环,因此确实是O(n)时间复杂度。

4. 实际编码中的陷阱

4.1 字符计数处理

一个常见的错误是没有正确处理字符计数。比如:

# 错误示例 if c in need: # 这样会漏掉need中计数为0的情况 need_cnt -= 1

正确的做法是直接检查need[c] > 0,因为我们的need字典会记录所有字符(包括计数为0的)。

4.2 指针移动顺序

另一个易错点是左指针移动时的顺序:

# 错误顺序 left += 1 need[s[left]] += 1 # 这里会漏掉最初的s[left]

应该先处理当前left位置的字符,再移动指针。

5. 性能优化技巧

5.1 提前终止

当找到长度等于T长度的子串时,可以直接返回,因为不可能有更短的解:

if right - left + 1 == len(t): return s[left:right+1]

5.2 使用数组替代哈希表

对于ASCII字符集,可以用固定大小的数组代替哈希表,减少哈希计算开销:

need = [0] * 128 for c in t: need[ord(c)] += 1

6. 复杂度对比

方法时间复杂度空间复杂度适用场景
暴力解法O(n²)O(m)不推荐
滑动窗口O(n)O(m)推荐
优化滑动窗口O(n)O(1)字符集固定时

7. 扩展思考

这个问题可以延伸出多个变种:

  1. 允许字符顺序不一致但必须连续
  2. 允许部分字符缺失(最多允许k个字符不匹配)
  3. 寻找包含T所有字符的最长子串(而不是最短)

每种变种都可以基于滑动窗口的思想进行调整,只需要修改窗口收缩的条件和结果记录方式。

8. 实际应用场景

这种算法在以下场景中非常实用:

  1. 文本编辑器的搜索高亮功能
  2. 基因序列匹配
  3. 网络流量模式识别
  4. 数据流中的关键字检测

掌握滑动窗口技巧不仅能解决这道LeetCode题目,还能应对许多实际的字符串处理需求。我建议在理解基础解法后,尝试自己实现几个变种问题,这样可以更深入地掌握算法的核心思想。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进