Featured image of post 滑动窗口

滑动窗口

复习了一下滑动窗口的算法,选了几道题,记一下代码和解题思路。

序言

滑动窗口一直以来都是我不曾注意到的,直到上班之后和同事聊天的时候才知道有这么个算法。恰巧最近做题的时候经常碰到,就专门整理一下。 本文的题目全都来自LeetCode,题单则是来自灵茶山艾府题集 最后一题没做,因为我没看懂题目。alt text

题解和代码

795. 区间子数组个数

alt text

题目分析

题目要求最大元素处于[left, right]之间,这就意味着,如果nums[i] > right,那么nums[i]所在的子数组都是不符合条件的,就都得去掉,由此可以看出,大于right的元素都是用来分隔的。 第二,如果nums[i] < left,那么nums[i]自身是不可以作为一个独立的子数组的。


分析示例2:由于9大于right(8),所以9把数组分成了两个部分,且互不干扰。分别是[2]和[2, 5, 6] 对于[2],有且仅有一个元素,对于[2, 5, 6],则是[2], [2, 5], [2, 5, 6], [5], [5, 6], [6]共6个子数组。 可以思考一下,如果[2, 5, 6]再加一个7,会多出多少个解呢,答案是以6结尾的数组加上单独的7,一共是4个。 以6结尾的数组,肯定是6之前的元素数量,也就是数组长度了。同理,对于处于[left, right]之间的元素,我们的规律已经找到了。


如果新增的不是7,而是1呢,很显然,1不能独立作为一个子数组,所以新增的是三个解 如果再次新增一个1,变为[2, 5, 6, 1, 1],那么第二个1可以新增多少个解呢,答案也是3个,因为比left小的元素,不能作为独立的子数组所以必须依赖最后一次出现的处于[left, right]中的元素。

代码实现

展开代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
	int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
		int n = nums.size();
		int ans = 0, l = 0, r = -1;
		for (int i = 0; i < n; i++) {
			if (nums[i] > right)	l = i + 1;
			if (nums[i] >= left)	r = i;
			ans += r - l + 1;
		}
		return ans;
	}
};

最后贴一下战绩: alt text

1658. 将 x 减到 0 的最小操作数

alt text

题目分析

题目的意思是可以从数组首尾任选一个数,使得选中的数恰好等于x。 按照题目的意思来肯定不好做,但是我们可以反过来,从数组当中去掉一个子数组,使得子数组的和恰好等于,sum(nums) - x。 所以这个就恰好无缝衔接到了滑动窗口。

代码实现

展开代码
 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
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
	int minOperations(vector<int>& nums, int x) {
		int n = nums.size();
		int sum = accumulate(nums.begin(), nums.end(), 0);
		if (sum < x)	return -1;
		if (sum == x)	return n;
		int ans = -1;
		int target = sum - x;
		int left = 0, right = 0;
		int index = 0;
		int cur_sum = 0;
		while (true) {
			while (cur_sum < target && index < n) {
				cur_sum += nums[index];
				index++;
			}
			do {
				if (cur_sum == target)	ans = max(ans, index - left);
				cur_sum -= nums[left];
				left++;
			} while (cur_sum >= target && left < index);
			if (index >= n) break;
		}
		return ans == -1 ? -1 : n - ans;
	}
};

贴战绩: alt text

1234. 替换子串得到平衡字符串

alt text

题目分析

这个题目还是比较简单的,QWER本来的比例是相等的,如果有字母多了,那就必然有字母少了,我们只需要找到一段子串,至少包含多出来的那些字母就行了。

代码实现

展开代码
 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
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
	int balancedString(string s) {
		int n = s.length();
		int type[128] = {0};
		type['Q'] = 0;
		type['W'] = 1;
		type['E'] = 2;
		type['R'] = 3;
		int cnt[4] = {0};
		int cnt_now[4] = {0};
		int c = 0;
		for (int i = 0; i < n; i++) {
			c = type[s[i]];
			cnt[c]++;
		}
		vector<int> overChars(4, 0);
		int len_over = 0;
		for (int i = 0; i < 4; i++) {
			c = i;
			if (cnt[c] > (n / 4)) {
				overChars[c] = cnt[c] - (n / 4);
				len_over++;
			}
		}
		if (len_over == 0)	return 0;
		int left = 0, right = 0, res = n;
		int over_cnt = 0;
		while (right < n) {
			while (right < n && over_cnt < len_over) {
				c = type[s[right]];
				cnt_now[c]++;
				if (cnt_now[c] == overChars[c]) {
					over_cnt++;
				}
				right++;
			}
			while (left < n && over_cnt == len_over) {
				res = min(res, right - left);
				c = type[s[left]];
				cnt_now[c]--;
				if (cnt_now[c] == overChars[c] - 1) {
					over_cnt--;
				}
				left++;
			}
		}
		return res;
	}
};

贴战绩: alt text

1574. 删除最短的子数组使剩余数组有序

alt text

题目分析

题目要求我们找到一个子数组,删除它之后,剩余数组仍然有序。对于这个题目来说,如果有多段有序子数组的话,其实中间的都是要删掉的,我们的目标仅仅是把一头一尾拼接起来。 因为中间的段落一定会是在第一段最后的数要小的那个数之后的,所以不满足。同理和最后一段最开始的数相比也是,所以中间的都得删掉。我们就只需要比较一头一尾两段即可。

代码实现

展开代码
 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
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
	int findLengthOfShortestSubarray(vector<int>& arr) {
		int n = arr.size();
		int left = 0, right = n - 1, ans = n - 1;
		for (int i = n - 2; i >= 0; i--) {
			if (arr[i] <= arr[i + 1]) {
				right = i;
			}
			else {
				break;
			}
		}
		if (right == 0)	return 0;
		ans = right;
		for (int i = 0; i < n; i++) {
			left = i;
			while (right < n && arr[left] > arr[right]) {
				right++;
			}
			ans = min(ans, right - left - 1);
			if (i == n - 1 || arr[i] > arr[i + 1]) {
				break;
			}
		}
		return ans;
	}
};

贴战绩: alt text

2106. 摘水果

alt text

题目分析

题目要求我们能采摘的果子下标是有范围的,就是[startPos - k, startPos + k] 如果我们要采摘从[l, r]的果子,我们会用到多少步数,可以分情况讨论: 如果l和r在同一侧,就是max(abs(r - startPos), abs(startPos - l)) 如果l和r在不同侧,就是r - l + min(abs(r - startPos), abs(startPos - l))

代码实现

展开代码
 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
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
	int maxTotalFruits(vector<vector<int>>& fruits, int startPos, int k) {
		int n = fruits.size();
		int left = -1;
		int right = -1;
		for (int i = 0; i < n; i++) {
			if (fruits[i][0] >= startPos - k && left == -1)		left = i;
			if (fruits[i][0] <= startPos + k)					right = i;
		}
		int l = left;
		int r = left;
		if (left == -1 || right == -1)	return 0;
		int ans = 0;
		int nowStep = 0;
		int nowFruits = 0;
		int li = 0;
		int ri = 0;
		while (r <= right) {
			while (r <= right && nowStep <= k) {
				li = fruits[l][0];
				ri = fruits[r][0];
				if (ri <= startPos && li <= startPos || ri >= startPos && li >= startPos) {
					nowStep = max(abs(ri - startPos), abs(startPos - li));
				}
				else {
					nowStep = ri - li + min(abs(ri - startPos), abs(startPos - li));
				}
				nowFruits += fruits[r][1];
				r++;
				if (nowStep > k)	break;
				ans = max(ans, nowFruits);
			}

			while (l <= right && nowStep > k) {
				li = fruits[l + 1][0];
				ri = fruits[r - 1][0];
				if (ri <= startPos && li <= startPos || ri >= startPos && li >= startPos) {
					nowStep = max(abs(ri - startPos), abs(startPos - li));
				}
				else {
					nowStep = ri - li + min(abs(ri - startPos), abs(startPos - li));
				}
				nowFruits -= fruits[l][1];
				l++;
				if (nowStep <= k) {
					ans = max(ans, nowFruits);
					break;
				}
			}
		}
		return ans;
	}
};

贴战绩: alt text