滑窗算法另一种常见用法,如下:
给定一个字符串S和一个字符串T,请在S中找出包含T所有字母的最小子串。
输入:S="ADOBECODEBANC", T="ABC"
输出:"BANC"
这个问题无法按照(一)中的方法进行查找,因为它不是给定了窗口大小让你找对应的值,而是给定了对应的值,让你找最小的窗口。
但我们仍然可以使用滑窗算法,只是需要换一个思路。
1、定义初始窗口为最小窗口
既然是找最小的窗口,我们可先定义一个最小的窗口,长度为0。
首先比较一下当前窗口所在位置的字母,是否是T中的一个字母。
很明显A是ABC中的一个字母,也就是T所有字母的最小子串可能包含当前位置的S的值。
如果包含,我们开始扩大窗口,直到扩大后的窗口能够包含T所有字母。
2、向右滑动窗口
窗口不能满足包含T所有字母的要求时,向右滑动。
不能包含T所有字母
当指针指向‘D’字母时,窗口AD仍不能包含T所有字母,此时需要继续滑动。
3、继续滑动窗口,直到能包含T中所有字母
继续右滑扩大...直到窗口能包含T所有的字母。
可以包含 T 的所有字母
假设题目是在S中找出包含T所有字母的第一个子串,这时我们就已经解决问题了,但是题目是找到最小的字串,这就会存在问题。
(1)当前窗口内可能包含了一个更小的能满足题目要求的窗口;
(2)窗口没有滑动到的位置有可能包含了一个更小的能满足题目要求的窗口。
为了解决以上可能出现的问题,当我们找到第一个满足的窗口后,需要从左缩小窗口。
4、从左侧缩小窗口
窗口从左侧开始缩小之后,结果会有两种情况:
(1)如果缩小后的窗口仍满足包含T所有字母的要求,则当前窗口可能是最小能满足题目的窗口,
储存下来之后,继续从左开始缩小窗口。
(2)如果缩小后的窗口不能满足包含T所有字母的要求,则缩小窗口停止,并从右边开始扩大窗口。
在本题中,当左侧开始缩小后,指针将指向‘D’字母,这时窗口将不能包含T的所有字母。
DOBEC不包含T的所有字母
当窗口不能包含T所有字母时,需要向右移动窗口,直到能包含T所有字母,如下。
DOBECODEBA 包含T的所有字母
不断重复上面的步骤,直到窗口滑动到最右边,且找不到合适的窗口为止。最小满足的窗口就是我们要找的S中包含T所有字母的最小子串。
BANC包含T的所有字母,且是最小子串
/* JaveScript示例代码*/
/*
*param {string} s
*param {string} t
*return {string}
*/
var minWindow = function(s, t) {
const map = {};
for (let i = 0; i < t.leng; i++) { //用map将t中所包含的元素记录下来,map[A]=1...
if (map[t[i]]) {
map[t[i]]++;
} else {
map[t[i]] = 1;
}
}
let left = 0;
let right = 0;
let count = t.length;
let max = Number.MAX_SAFE_INTEGER;
let res = s;
while (right < s.length) {
if (map[s[right]] > 0) { //第二次满足条件时子串已经为DOBECODEBA,因为map[A]=1
count--;
}
map[s[right]]--; //第二次循环,不在T中的字母map[x]将为-1
right++;
while (count === 0) { //当count==0时,就在S中找到了包括T的子串,第一次为ADOBEC
if (right - left < max) {
max = right - left;
//第一次res为ADOBEC,第二次left为1,故res为DOBECODEBA
res = s.slice(left, right);
}
map[s[left]]++; //第一次后map[A]=1,第二次进来map[D]=-1+1=0
if (map[s[left]] > 0) {
//第二次map[D]不满足条件,继续循环,left将++,res将变为OBECODEBA,一直到CODEBA,跳出while
count++;
}
left++; //left右移缩小窗口
}
}
return max == Number.MAX_SAFE_INTEGER ? "" : res;
};
right - left < max的判断是为了确保获得的res是最小的子串。
算法时间复杂度为O(s+t)。