76. 最小覆盖子串
webVueBlog opened this issue · comments
I am ne zha / Jeskson commented
76. 最小覆盖子串
Description
Difficulty: 困难
Related Topics: 哈希表, 字符串, 滑动窗口
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
示例 3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
- 1 <= s.length, t.length <= 105
s
和t
由英文字母组成
**进阶:**你能设计一个在 o(n)
时间内解决此问题的算法吗?
Solution
Language: JavaScript
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
// 将需要匹配的字符放入字典表,存储结构为需要匹配的字符,以及字符出现的次数:[字符,次数]
// 利用双指针维护一个滑动窗口,不断移动右指针
// 判断右指针的字符是否与字典表中的匹配
// 是:将字典表中的次数-1,直到为0 (创建一个needType)记录需要匹配的字符数量,初始长度为Map的size,当对应的字符次数为0时,就减1
// 否:继续移动右指针
// 当needType的值为0时,就证明在当前窗口所有字符都匹配成功了
// 1.移动左指针,缩小滑动窗口的大小
// 2.移动过程中判断左指针指向的值是否为字典中值,如果是就证明匹配的值少了一个,这个需要更新Map中的次数,以及needType的数量
// 3.记录每次窗口中的字符,找到最小的返回
var minWindow = function(s, t) {
// 创建左指针
let l = 0
// 创建右指针
let r = 0
// 最后需要返回的最小长度子串
let res = ''
// 创建字典表
const m = new Map()
// 遍历需要匹配的字符
for (let i = 0; i < t.length; i++) {
const c = t[i]
// 放入字典表
m.set(c, m.has(c) ? m.get(c) + 1 : 1)
}
// 创建记录需要匹配的字符种类
let needType = m.size
// 遍历字符串
while (r < s.length) {
// 获取当前字符
const c = s[r]
// 如果是需要匹配的字符
if (m.has(c)) {
// 更新字典表中的次数-1
m.set(c, m.get(c) - 1)
// 如果次数为0,证明这个字符种类在当前窗口已经集齐了,needType-1
if (m.get(c) === 0) needType -= 1
}
// 当needType为0,证明所有需要匹配的字符串都已经在当前滑动窗口中
while (needType === 0) {
const c2 = s[l]
// 记录当前滑动窗口的字符
let newRes = s.slice(l, r + 1)
// 当新的窗口中字符长度小于上次的字符长度时,更新结果
// !res 是在结构值为空的时候需要更新一下第一次匹配的值
if (!res || newRes.length < res.length) res = newRes
// 如果左指针移动过程中出现,字典中的值证明需要匹配的字符已经脱离了当前窗口
if (m.has(c2)) {
// 更新表中需要出现的次数
m.set(c2, m.get(c2) + 1)
// 更新needType
if (m.get(c2) === 1) needType += 1
}
// 移动左指针
l++
}
// 移动右指针
r++
}
// 返回结果值
return res
}