每日算法练习:LeetCode 76. 最小覆盖子串 ✅

大家好,我是你们的算法小伙伴。今天我们来攻克一道滑动窗口的经典困难题 ------LeetCode 76. 最小覆盖子串。这道题是滑动窗口的「标杆题目」,完美体现了「右指针扩张、左指针收缩」的核心思想,同时考察哈希表计数、匹配状态维护等综合能力,是面试中字符串处理的高频难题。


题目描述

给你两个字符串 st ,返回 s 中涵盖 t 所有字符的最小长度子串 。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。
  • 进阶:设计一个在 O(m + n) 时间复杂度内解决此问题的算法。

示例 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.length
  • n == t.length
  • 1 <= m, n <= 10^5
  • st 由英文字母组成

解题思路

核心思路:滑动窗口 + 双哈希表

这道题的核心是动态维护一个窗口 ,保证窗口内包含 t 的所有字符(次数不低于 t 中的次数),并在所有合法窗口中找到长度最小的那个。

核心步骤:
  1. 统计目标字符 :用数组 need 统计 t 中每个字符需要的次数
  2. 计算字符种类required = t不同字符的种类数量
  3. 右指针扩张:不断加入字符,更新窗口内计数
  4. 匹配判断 :用 match 记录已满足次数要求的字符种类数
  5. 左指针收缩 :当 match == required 时,不断收缩左指针,更新最小窗口
  6. 返回结果:最终截取最小窗口子串

代码实现(O (m+n) 时间复杂度)

复制代码
class Solution {
    public String minWindow(String s, String t) {
        if (t.length() > s.length()) {
            return "";
        }

        int[] need = new int[128];
        int[] window = new int[128];

        // 统计t中每个字符的出现次数
        for (char c : t.toCharArray()) {
            need[c]++;
        }

        // 计算t中不同字符的种类数
        int required = 0;
        for (int count : need) {
            if (count > 0) {
                required++;
            }
        }

        int left = 0, right = 0;
        int match = 0; // 已匹配的字符种类数
        int start = 0, minLen = Integer.MAX_VALUE;

        while (right < s.length()) {
            // 右指针扩张
            char c = s.charAt(right);
            window[c]++;
            if (need[c] > 0 && window[c] == need[c]) {
                match++;
            }
            right++;

            // 当所有字符种类都匹配时,收缩左指针
            while (match == required) {
                // 更新最小窗口
                if (right - left < minLen) {
                    minLen = right - left;
                    start = left;
                }

                // 左指针收缩
                char d = s.charAt(left);
                if (need[d] > 0 && window[d] == need[d]) {
                    match--;
                }
                window[d]--;
                left++;
            }
        }

        return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
    }
}

代码详解

1. 数组优化说明

  • 由于字符仅为英文字母(ASCII 128 个),用 int[128] 数组代替 HashMap,时间复杂度更优,空间复杂度为 O(1)
  • need[]t 中每个字符需要出现多少次
  • window[]:当前窗口内每个字符出现了多少次
  • requiredt 中一共有多少种不同字符(必须全部匹配才算合法)
  • match当前窗口内,次数已经达标的字符种类数
  • left / right:滑动窗口双指针
  • start / minLen:记录最小窗口的起点和长度

2. 核心变量 match

match 表示当前窗口内,次数完全满足 t 要求的字符种类数

  • match 等于 t 中不同字符的种类数时,窗口合法。
  • 每次右指针加入字符时,若该字符次数刚好达标,match++
  • 每次左指针移除字符时,若该字符次数不再达标,match--

3. 核心逻辑

  • 右指针一直往右走,把字符加入窗口
  • 加入后如果某个字符次数刚好达标match++
  • match == required窗口完全合法
  • 进入内层 while拼命收缩左指针,尝试找到更小窗口
  • 收缩时如果某个字符次数不达标了match--,退出循环
  • 最终记录最小窗口 → 截取返回

示例 1

模拟:s = "ADOBECODEBANC", t = "ABC"

  • need 数组:A:1, B:1, C:1,其他为 0。
  • 右指针扩张到 right=5(字符 C)时,match=3,窗口合法,开始收缩左指针,得到第一个合法窗口 "ADOBEC",长度 6。
  • 右指针继续扩张,直到 right=10(字符 A),match=3,收缩左指针,得到最小窗口 "BANC",长度 4。
  • 最终返回 "BANC",与示例结果一致。

复杂度分析

指标 复杂度 说明
时间复杂度 O(m+n) 右指针遍历 s 一次(O(m)),左指针最多遍历 s 一次(O(m)),统计 t 一次(O(n)),总复杂度线性
空间复杂度 O(1) 数组大小固定为 128,与输入规模无关

高频易错点总结

  1. 边界处理t 长度大于 s 时,直接返回空字符串。
  2. match 统计错误 :必须保证字符次数刚好等于 目标次数时才 match++,移除时次数等于 目标次数时才 match--
  3. 窗口长度计算right - left(因为 right 已经右移一位,无需 +1)。
  4. 数组初始化needwindow 数组初始化为 0,无需手动填充。
  5. 最终返回 :必须判断 minLen 是否更新,未更新则返回空字符串。

总结

这道题是滑动窗口的经典模板题 ,核心思想是「右指针扩张找合法窗口,左指针收缩找最小窗口」,用 match 变量快速判断窗口合法性,完美实现了 O(m+n) 的线性时间复杂度,完全满足进阶要求。

  • 面试优先级:这是面试中滑动窗口的必背题目,代码结构清晰,逻辑严谨,是考察字符串处理能力的标杆题。
  • 拓展应用:该模板可以直接套用在「最小覆盖子串」「字符串排列」「找到所有字母异位词」等同类滑动窗口题目中。

今天的每日算法练习就到这里,我们明天再见!👋

相关推荐
Wect2 小时前
LeetCode 149. 直线上最多的点数:题解深度剖析
前端·算法·typescript
qianpeng8972 小时前
运动声源的到达结构仿真
算法
费曼学习法2 小时前
线段树:区间查询的"终极武器",一文看透高效范围统计
算法
wayz112 小时前
Day 2:线性回归原理与正则化
算法·机器学习·数据分析·回归·线性回归
QQ676580082 小时前
基于yolo26算法的水下目标检测图像数据集 海洋生物识别 海胆识别 海龟识别数据集 海洋生物监测与保护工作 潜水作业安全辅助系 水下环境感知第10408期
算法·目标检测·水下目标检测·海洋生物识别·海胆 海龟识别·海洋生物监测与保护工作·潜水作业安全辅助 水下环境感知
Via_Neo2 小时前
判断字符串前缀(26年蓝桥杯JAVA B组)
java·职场和发展·蓝桥杯
七颗糖很甜3 小时前
基于 OpenCV 的 FY2 云顶图云块追踪算法实现
人工智能·opencv·算法
__Wedream__3 小时前
NTIRE 2026 Challenge on Efficient Super-Resolution——冠军方案解读
人工智能·深度学习·算法·计算机视觉·超分辨率重建
FL16238631293 小时前
基于深度学习mediape实现人员跌倒人体姿势跌倒检测算法源码+说明文件
人工智能·深度学习·算法