参阅基础

简介

遇到与给定对象的连续子区间有关的问题时,一个很容易联想到的技巧就是滑动窗口。

滑窗的思路非常简单,就是维护一个窗口,不断滑动,然后更新答案。

但滑窗的难点不是算法的思路,而是各种细节问题。比如如何向窗口中添加新元素,如何缩小窗口,在窗口滑动的哪个阶段更新结果。

以下这套滑动窗口算法的代码框架,标记了实现中需要注意的细节,和输出 debug 的位置。以后遇到相关的问题,只需要默写出该框架,然后改三个地方就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const slidingWindow = (s) => {
const win = [];

let left = 0,
right = 0;

while (right < s.length) {
// c 是将移入窗口的字符
const c = s[right];
// 增大窗口
right++;

// 进行窗口内数据的一系列更新
// ...

/* debug 输出的位置 */
console.log("window", win);

while(win needs shrink){
// d 是将移出窗口的字符
const d = s[left];
// 缩小窗口
left++;
// 进行窗口内数据的一系列更新
// ...
}
}
};

其中两处 ... 表示的更新窗口数据的地方,直接往里面填逻辑就行。而且这两处 ... 的操作分别是扩大和缩小窗口的更新操作,它们的操作是完全对称的。

另外,虽然滑动窗口代码框架中有一个嵌套的 while 循环,但算法的时间复杂度依然是 O(N)
,其中 N 是输入字符串/数组的长度。因为字符串(或数组)中的每个元素都只会进入窗口一次,然后被移出窗口一次,不会有某些元素多次进入和离开窗口的情况。所以算法的时间复杂度就和字符串(或数组)的长度成正比。

相关习题

LeetCode 76. 最小覆盖子串

题目

来源:https://leetcode.cn/problems/minimum-window-substring/
难度:困难

我的题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
let needMap = new Map()

let win = new Map()
let vaild = 0
let start = 0,len = 0;
let isFirst = true

for(index in t){
if(needMap.has(t[index])){
needMap.set(t[index],needMap.get(t[index]) + 1)
}else{
win.set(t[index],0)
needMap.set(t[index],1)
}
}

let needLen = needMap.size
let left = 0,right = 0;

while(right < s.length){
const c = s[right];
right++;

// 更新数据
if(needMap.get(c)){
win.set(c,win.get(c) + 1)

if(win.get(c) == needMap.get(c)){
vaild ++;
}
}

console.log(needLen,vaild)
while(needLen === vaild){
const d = s[left];

if(isFirst){
start = left
len = right
isFirst = false
}else{
if(right - left < len){
start = left
len = right - left
}
}

left++;
//更新数据
if(needMap.get(d)){
win.set(d,win.get(d) - 1)

if(win.get(d) < needMap.get(d)){
vaild --;
}
}
}
}

return s.substring(start,start + len)
};