LongDz 的数字文墨提案

76. 最小覆盖子串

4 min

题目

76. 最小覆盖子串 困难

解法:滑动窗口

思路

核心思想是使用滑动窗口配合字符计数数组动态维护覆盖状态。

正确性在于:当右指针扩展窗口时,通过need数组精确追踪每个字符的覆盖缺口;当窗口满足条件时,左指针收缩过程中通过need[d]>0判断是否破坏覆盖条件,确保每次收缩都找到当前右边界下的最小窗口。

  • Step 1: 初始化need数组记录t字符需求,need_count记录剩余需匹配字符数
  • Step 2: 右指针移动时更新字符计数,当need_count归零触发窗口收缩
  • Step 3: 左指针收缩时更新最小窗口,并通过need[d]>0判断覆盖条件破坏点

复杂度

  • 时间复杂度: O(m+n)O(m+n)
  • 空间复杂度: O(1)O(1)

代码

class Solution {
public:
    string minWindow(string s, string t) {
        // ASCII 字符计数数组
        vector<int> need(128, 0);
        for (char c : t) need[c]++;
        int need_count = t.size(); // 总共还需要匹配的字符数量
        int left = 0, right = 0;
        int min_len = INT_MAX;
        int start = 0;
        
        while (right < s.size()) {
            // 1. 进窗口逻辑
            char c = s[right];
            // 如果当前字符是 t 需要的(计数大于0),则有效匹配数减1
            if (need[c] > 0) {
                need_count--;
            }
            // 无论是不是需要的字符,都减1
            // 如果是无关字符,会变成负数;如果是多余的某个所需字符,也会变成负数或0
            need[c]--;
            right++;
            
            // 2. 窗口收缩逻辑(当所有字符都找齐了)
            while (need_count == 0) {
class Solution {
public:
    string minWindow(string s, string t) {
        // ASCII 字符计数数组
        vector<int> need(128, 0);
        for (char c : t) need[c]++;
        int need_count = t.size(); // 总共还需要匹配的字符数量
        int left = 0, right = 0;
        int min_len = INT_MAX;
        int start = 0;
        
        while (right < s.size()) {
            // 1. 进窗口逻辑
            char c = s[right];
            // 如果当前字符是 t 需要的(计数大于0),则有效匹配数减1
            if (need[c] > 0) {
                need_count--;
            }
            // 无论是不是需要的字符,都减1
            // 如果是无关字符,会变成负数;如果是多余的某个所需字符,也会变成负数或0
            need[c]--;
            right++;
            
            // 2. 窗口收缩逻辑(当所有字符都找齐了)
            while (need_count == 0) {
                // 更新最小覆盖子串结果
                if (right - left < min_len) {
                    min_len = right - left;
                    start = left;
                }
                
                // 尝试移出左边界字符
                char d = s[left];
                need[d]++; //把之前的减法补回来
                
                // 如果补回来之后 > 0,说明刚才移除的是一个“必须且不富余”的字符
                // 于是窗口变得不再满足条件,需要重新去右边找
                if (need[d] > 0) {
                    need_count++;
                }
                left++;
            }
        }
        
        return min_len == INT_MAX ? "" : s.substr(start, min_len);
    }
};