题目
题目来源:力扣(LeetCode)
给你一个字符串 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 的子串中,
因此没有符合条件的子字符串,返回空字符串。
思路分析
滑动窗口
用 left, rigth 表示滑动窗口的左边界和右边界,通过改变 left, right 来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度right - left + 1,这些长度中的最小值就是要求的结果。
- 我们在字符串 S 中使⽤双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left,
right] 称为⼀个「窗⼝」。 - 我们先不断地增加 right 指针扩⼤窗⼝ [left, right],直到窗⼝中的字符串符合要求(包含了 t 中的
所有字符) - 我们停⽌增加 right,转⽽不断增加 left 指针缩⼩窗⼝ [left, right],直到窗⼝中的字符串不再符合
要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新⼀轮结果 - 重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头
 
/*** @param {string} s* @param {string} t* @return {string}*/var minWindow = function(s, t) {// 需要的let need = {};// 窗口中的字符let window = {};for (let a of t) {// 统计需要的字符need[a] = (need[a] || 0) + 1;}// 左右指针let left = 0,right = 0;let valid = 0;// 最小覆盖子串的起始索引及长度let start = 0,len = Number.MAX_VALUE;while (right < s.length) {// 即将移入窗口的字符let c = s[right];// 右移窗口right++;if (need[c]) {// 当前字符在需要的字符中,则更新当前窗口统计window[c] = (window[c] || 0) + 1;if (window[c] == need[c]) {// 当前窗口和需要的字符匹配时,验证数量增加1valid++;}}// 当验证数量与需要的字符个数一致时,就应该收缩窗口了while (valid == Object.keys(need).length) {// 更新最小覆盖子串if (right - left < len) {start = left;len = right - left;}//即将移出窗口的字符let d = s[left];// 左移窗口left++;if (need[d]) {if (window[d] == need[d]) {valid--;}window[d]--;}}}return len == Number.MAX_VALUE ? "" : s.substr(start, len);};
