当前位置: 首页 > news >正文

【LeetCode HOT100 】:最小覆盖子串——滑动窗口的经典应用题解

题目概述

题目名称:76. 最小覆盖子串
难度:困难
题目链接:https://leetcode.cn/problems/minimum-window-substring/

题目描述
给定两个字符串st,返回s中涵盖t所有字符的最小子串。如果不存在,则返回空字符串""

关键点

  • 涵盖意味着s子串中每个字符的数量必须不少于t中该字符的数量

  • 答案唯一且连续子串

  • 时间复杂度要求较高(数据规模可达 10^5)

示例

text

输入:s = "ADOBECODEBANC", t = "ABC" 输出:"BANC"

解题思路总览

本题属于滑动窗口的经典应用。核心思路是维护一个动态窗口,通过移动右指针扩展窗口直至覆盖t中所有字符,再移动左指针收缩窗口以寻找最短覆盖子串。

解法时间复杂度空间复杂度适用场景
滑动窗口 + 哈希表O(n)O(k)通用(k为字符集大小)
滑动窗口 + 数组O(n)O(128)仅限ASCII字符
优化滑动窗口(去冗余)O(n)O(n)字符重复率高的场景

解法一:滑动窗口 + 哈希表(通用解法)

核心思想

使用两个哈希表:

  • need:记录字符串t中每个字符的需求数量

  • window:记录当前窗口中每个字符的出现次数

维护一个valid变量,表示当前窗口中已满足需求量的字符种类数。当valid == need.size()时,说明当前窗口已覆盖t,此时尝试收缩左边界。

算法步骤

  1. 初始化left = 0valid = 0,最小长度minLen = ∞

  2. 右指针right遍历s

    • s[right]加入窗口

    • 如果该字符是需要的,且窗口内数量达到需求,valid++

    • valid == need.size()时:

      • 更新最短子串的起始位置和长度

      • 尝试移动左指针收缩窗口:

        • 左移前,如果左边界字符是需要的且窗口内数量刚好等于需求,valid--

        • 左边界字符移出窗口,left++

  3. 返回最短子串

代码实现(Python)

python

class Solution: def minWindow(self, s: str, t: str) -> str: from collections import defaultdict need = defaultdict(int) for c in t: need[c] += 1 window = defaultdict(int) left = 0 valid = 0 start = 0 min_len = float('inf') for right in range(len(s)): c = s[right] if c in need: window[c] += 1 if window[c] == need[c]: valid += 1 while valid == len(need): if right - left + 1 < min_len: min_len = right - left + 1 start = left d = s[left] left += 1 if d in need: if window[d] == need[d]: valid -= 1 window[d] -= 1 return s[start:start + min_len] if min_len != float('inf') else ""

代码实现(Java)

java

class Solution { public String minWindow(String s, String t) { Map<Character, Integer> need = new HashMap<>(); Map<Character, Integer> window = new HashMap<>(); for (char c : t.toCharArray()) { need.put(c, need.getOrDefault(c, 0) + 1); } int left = 0, valid = 0; int start = 0, minLen = Integer.MAX_VALUE; for (int right = 0; right < s.length(); right++) { char c = s.charAt(right); if (need.containsKey(c)) { window.put(c, window.getOrDefault(c, 0) + 1); if (window.get(c).equals(need.get(c))) { valid++; } } while (valid == need.size()) { if (right - left + 1 < minLen) { minLen = right - left + 1; start = left; } char d = s.charAt(left); left++; if (need.containsKey(d)) { if (window.get(d).equals(need.get(d))) { valid--; } window.put(d, window.get(d) - 1); } } } return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen); } }

解法二:滑动窗口 + 数组(ASCII优化版)

核心思想

由于题目中的字符由英文字母组成,可以使用长度为 128 的数组代替哈希表,效率更高。数组下标直接对应字符的 ASCII 码。

代码实现(Python)

python

class Solution: def minWindow(self, s: str, t: str) -> str: need = [0] * 128 for c in t: need[ord(c)] += 1 window = [0] * 128 left = 0 count = 0 # 已满足需求的字符总数(非种类) min_len = float('inf') start = 0 for right in range(len(s)): idx = ord(s[right]) window[idx] += 1 if window[idx] <= need[idx]: count += 1 while count == len(t): if right - left + 1 < min_len: min_len = right - left + 1 start = left left_idx = ord(s[left]) if window[left_idx] <= need[left_idx]: count -= 1 window[left_idx] -= 1 left += 1 return s[start:start + min_len] if min_len != float('inf') else ""

注意:此版本使用count统计已满足需求的字符总数而非种类数,逻辑略有不同但效果相同。


解法三:优化滑动窗口(预处理有效字符)

核心思想

当字符串s很长但t很短时,s中大部分字符可能都不在t中。可以预先过滤掉这些无效字符,只保留s中出现在t里的字符及其下标,然后在过滤后的序列上应用滑动窗口。

适用场景

  • s长度很大(如 10^5)

  • t长度很小

  • s中属于t的字符占比很低

算法步骤

  1. 遍历s,记录所有属于t的字符及其在s中的下标

  2. 在过滤后的字符序列上执行滑动窗口

  3. 根据下标还原原字符串中的子串位置

代码实现(Python)

python

class Solution: def minWindow(self, s: str, t: str) -> str: from collections import defaultdict, Counter need = Counter(t) filtered = [(i, ch) for i, ch in enumerate(s) if ch in need] left = 0 window = defaultdict(int) valid = 0 start = 0 min_len = float('inf') for right in range(len(filtered)): idx, ch = filtered[right] window[ch] += 1 if window[ch] == need[ch]: valid += 1 while valid == len(need) and left <= right: left_idx, left_ch = filtered[left] curr_len = idx - left_idx + 1 if curr_len < min_len: min_len = curr_len start = left_idx if window[left_ch] == need[left_ch]: valid -= 1 window[left_ch] -= 1 left += 1 return s[start:start + min_len] if min_len != float('inf') else ""

复杂度分析对比

解法时间复杂度空间复杂度说明
哈希表滑动窗口O(n)O(k)k为字符集大小,通用性最强
数组滑动窗口O(n)O(128)常数级空间,执行效率最高
过滤优化版O(n + m)O(m)m为过滤后字符数,适合特定场景

注:n = len(s),m = len(t)


易错点总结

  1. valid的含义:代表已满足数量要求的字符种类数,而非字符总数。当某字符的窗口计数恰好等于需求计数时才增加valid

  2. 窗口收缩条件:必须使用while而非if,因为收缩后窗口可能仍然满足覆盖条件,需要持续收缩。

  3. 边界处理:当min_len仍为无穷大时,返回空字符串。

  4. 字符重复处理t中可能包含重复字符(如"aa"),需要确保窗口中有至少两个'a'


相关题目推荐

题目难度关联点
3. 无重复字符的最长子串中等滑动窗口基础
438. 找到字符串中所有字母异位词中等固定窗口滑动
567. 字符串的排列中等窗口内字符计数匹配
30. 串联所有单词的子串困难多模式串滑动窗口

参考链接

  • 题目链接:LeetCode 76. 最小覆盖子串

  • 官方题解


如果这篇文章对你有帮助,欢迎点赞、收藏、关注!🎉

http://www.jsqmd.com/news/637339/

相关文章:

  • 别再对着空白界面发呆了!手把手教你用GNURadio Companion(GRC)画出第一个信号流图
  • GoB插件深度解析:3步实现Blender与ZBrush专业级数据传输
  • TortoiseGit与Gerrit完美配合:Windows下的代码Review避坑指南
  • 2026年评价高的水泥草坪砖长期合作厂家推荐 - 行业平台推荐
  • Harness 中的流式请求与响应多路复用
  • 2026年分体法兰厂家有哪些,分体法兰/SAE法兰/扩口法兰/法兰夹/内螺纹法兰/方法兰,分体法兰采购怎么选择 - 品牌推荐师
  • Qwen3.5-9B-AWQ-4bit多场景方案:跨境电商商品图合规检测(文字/Logo/尺寸)
  • 小米、红米电视系统更新固件ROM合集分享 电视刷机升级固件
  • ArcGIS用户必看:用CC工具箱一键搞定面要素四至点提取与坐标写入
  • SITS2026联合17家头部AI工厂达成共识:大模型工程化已进入“SLA驱动时代”,这6项SLO指标你达标了吗?
  • 利用Chord - Ink Shadow自动化批改作业:教育领域的AI助手实践
  • 块状链表的长度
  • Android音频无线传输终极指南:如何免费实现手机声音实时同步到电脑
  • 从零开始:手把手教你编写第一个CMakeLists.txt(完整实战指南)
  • 3步完成B站M4S视频转换:免费跨平台工具完整指南
  • After Effects (AE)2026超详细保姆级下载安装教程 附软件功能详解(新手零基础适用)
  • CRaxsRat v7.4 实战部署:从零搭建远程管理测试环境
  • 卸船机市场调研:2026 - 2032年复合增长率(CAGR)为2.7%
  • 【一天一个计算机知识】Cyber骇客对数据流的 算力操纵与指令集 ——【<algorithm>头文件】从算法的出处和算法的角度带你解读<algorithm>的内容与机制
  • 如何用Python构建智能交易策略:PyBroker量化框架完整指南
  • PyTorch 2.8镜像科研展示:气候模型输出→AI生成可视化动态气象视频
  • PowerPaint-V1商业修图实战:批量处理产品图,提升工作效率
  • CTF解题实战:手把手教你用JSFuck在线解码器搞定LitCTF 2023那道‘天书’题
  • Handof f协议:多Agent任务交接机制
  • 电视盒子刷机固件合集大全 电视网络机顶盒机顶盒最新更新固件
  • 从Q15到Q31:电机控制算法中的定点数精度权衡与实战选型
  • CodeFormer深度解析:基于代码本查找Transformer的鲁棒盲脸修复实战指南
  • 用Matlab App Designer给杨氏双缝干涉实验做个交互式GUI(附完整源码)
  • 如何利用Keyviz打造专业级键鼠操作可视化演示
  • Teledyne LeCroy HVD3106A 高压差分探头1kV、120 MHz 带自动归零功能