Skip to main content

最小覆盖子串

题目

给你一个字符串 st, 请返回 s 中涵盖 t 所有字符的最小子串. 如果 s 中不存在涵盖 t 所有字符的子串, 则返回空字符串.

注意: 如果 s 中存在这样的子串, 我们保证它是唯一的答案.

提示:
  • m == s.length
  • n == t.length
  • 1 <= m, n <= 10⁵
  • st 由英文字母组成
示例

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

输出: BANC

题解

这是个很经典的滑动窗口问题, 该题跟 438. 找到字符串中所有字母异位词 解法一致.

  1. 首先创建一个名叫 needMap 的哈希表, 用于映射 t 的每个元素
  2. 定义一个 count 的变量, 初始化为 0, 当 windowMap[s[right]] === needMap[s[right]] 时, 让 count++
  3. 因此当 count === needMap.size 时, 就保证了 windowMap 中已经覆盖了 t 字符串
  4. 接着就可以缩小窗口了, 缩小窗口的代码跟扩大窗口的代码相反, 即直到窗口中的字符串不再符合要求, left 不再继续移动
  5. 重复这个过程, 直到 right 到了最右边退出循环
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function (s, t) {
let minStr = ''

const needMap = new Map()
for (const ch of t) {
needMap.set(ch, needMap.has(ch) ? needMap.get(ch) + 1 : 1)
}

const windowMap = new Map()
let start = 0
let end = 0
let count = 0

while (end < s.length) {
const endCh = s[end++]

if (needMap.has(endCh)) {
windowMap.set(endCh, windowMap.has(endCh) ? windowMap.get(endCh) + 1 : 1)
if (needMap.get(endCh) === windowMap.get(endCh)) count++
}

// 当 count === needMap.size, 说明找到了一个覆盖子串
// 这个时候就可以收缩窗口了
while (count === needMap.size) {
const subStr = s.slice(start, end)
if (minStr.length === 0 || subStr.length < minStr.length) minStr = subStr

const startCh = s[start++]

if (needMap.has(startCh)) {
if (needMap.get(startCh) === windowMap.get(startCh)) count--
windowMap.set(startCh, windowMap.get(startCh) - 1)
}
}
}

return minStr
}