// 76. 最小覆盖子串// 给你一个字符串 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 的子串中,// 因此没有符合条件的子字符串,返回空字符串。/*** @param {string} s* @param {string} t* @return {string}*/// 题解// 找出所有子串// 找出最短子串// 双指针维护一个滑动窗口var minWindow = function(s, t) {// 定义左右指针或者叫做快慢指针,起始都是从零开始的let l = 0;let r = 0;// 创建一个集合用来存储t,键为item, 值为item出现的次数let map = new Map();for(let item of t) {map.set(item, map.get(item) ? map.get(item) + 1 : 1)}let mapType = map.size;let res = '';// 遍历Swhile(r < s.length) {const c = s[r];// 如果包含当前值,就更新mapif(map.has(c)) {map.set(c, map.get(c) - 1);// 什么时候停止呢?当map的value 小于等于0时,这里我们不用再去遍历if(map.get(c) === 0) {mapType -= 1}}// 左指针怎么移动,// 左指针什么时候移动呢?就是当我们找到在S中找到了所有t,也就是mapType === 0的时候while(mapType === 0) {const newRes = s.substring(l, r+1);if(!res || newRes.length < res.length) {res = newRes}// 获取左指针当前值const c2 = s[l];// 如果左指针存在于map中,给value + 1if(map.has(c2)) {map.set(c2, map.get(c2) + 1);if(map.get(c2) === 1) {mapType += 1}}l += 1}r += 1;}return res};let s = "ADOBECODEBANC", t = "ABC";console.log(minWindow(s, t));
时间复杂度:O(M + N) m 就是t的长度, n 就是s长度
空间复杂度:新建了一个字典,最坏的情况就是t的长度, O(k) k就是t里面不同字符的个数
