在问 · 算法题解

76. 最小覆盖子串

题目描述

输入字符串 s 和 t,长度分别为 m 和 n。

在 s 中找一段连续子串,要求 t 里的每个字符都在其中出现,且出现次数不少于它在 t 中的次数。

返回满足要求的最短子串。如果找不到,返回空串 ""。题目保证最短子串唯一。

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

解释:"BANC" 含有 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
  • s 和 t 只含英文字母。

进阶:能否在 O(m + n) 时间内求解?

解题思路

右指针扩展并用 win 统计频次;have 记录频次达到 need 要求的字符种类数。当 have==need_k 时,先更新最短区间 best,再右移 left,直到覆盖失效。最后按 best 保存的半开区间切片返回。

为什么正确

每次进入收缩循环时,have==need_k,窗口覆盖 t 的所有字符及其次数;best 保存已检查合法窗口中最短窗口的 (长度,左端点,右端点后一位)。

复杂度分析

设 D_s、D_t 分别为 s、t 的不同字符数,L 为返回子串长度。时间 O(|s|+|t|);辅助空间 O(D_s+D_t),返回切片另占 O(L),含输出空间 O(D_s+D_t+L)。

易错点

字符计数要包含重复字符,不能只用集合判断覆盖。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

from collections import Counter

class Solution:

    def minWindow(self, s,t):
        need=Counter(t); win={}; have=0; need_k=len(need); left=0; best=(10**9,0,0)
        for right,ch in enumerate(s):
            win[ch]=win.get(ch,0)+1
            if ch in need and win[ch]==need[ch]: have+=1
            while have==need_k:
                if right-left+1 < best[0]: best=(right-left+1,left,right+1)
                c=s[left]; win[c]-=1
                if c in need and win[c] < need[c]: have-=1
                left+=1
        return s[best[1]:best[2]] if best[0]<10**9 else ""

载入编辑器,自己跑一遍 →

滑动窗口 专题的其他题目

查看 滑动窗口 全部题目 →