五月集训(第06天)——滑动窗口
滑动窗口
1. 1984. 学生分数的最小差值
思路:
先对nums
排序,利用长度为k
的滑动窗口在数组内向右平移,每次算出窗口左右端的差即为分数差值,取其中最小值返回即可。
class Solution {
public:
int minimumDifference(vector<int>& nums, int k) {
int n = nums.size();
int i = 0, j = -1;
int ans = 1e5+10;
sort(nums.begin(), nums.end());
while (j < n - 1) {
j ++;
if (j - i + 1 > k) i++;
if (j - i + 1 == k) ans = fmin(ans, nums[j] - nums[i]);
}
return ans;
}
};
2. 1876. 长度为三且各字符不同的子字符串
思路:
利用长度为3的滑动窗口在字符串上向右平移,判断窗口内的三个字符是否相等,不相等则结果加一。
class Solution {
public:
int countGoodSubstrings(string s) {
int len = s.length();
int i = 0, j = -1;
int ans = 0;
while (j < len - 1) {
j ++;
if (j - i + 1 > 3) i++;
if (j - i + 1 == 3){
if (s[j] != s[j - 1] && s[j] != s[j - 2] && s[j - 1] != s[j - 2])
ans ++;
}
}
return ans;
}
};
3. 1839. 所有元音按顺序排布的最长子字符串
思路:
利用一个变长的滑动窗口,窗口向右扩展
1) 如果新加入的元音字母是当前元音字母的下一个,则type++
2) 如果新加入的元音字母与前元音字母相同,则继续向后扩展
3) 其他情况视为不合法的扩展,收紧窗口,重新扩张left = right
class Solution {
public:
int get_index(char ch) {
switch (ch) {
case 'a': return 1;
case 'e': return 2;
case 'i': return 3;
case 'o': return 4;
case 'u': return 5;
}
return 0;
}
int longestBeautifulSubstring(string word) {
int len = word.length();
int left = 0, right = 0;
int ans = 0;
int type = 1;
while (right < len - 1) {
right ++;
if (get_index(word[right]) - get_index(word[right - 1]) == 1) {
type ++;
} else if (get_index(word[right]) - get_index(word[right - 1]) != 0){
left = right;
type = 1;
}
if (type == 5 && word[right] == 'u') ans = max(ans, right - left + 1);
//printf("a='%c' right=%d type=%d ans=%d\n", word[right], right, type, ans);
}
return ans;
}
};
4. 1052. 爱生气的书店老板
思路:
利用前缀和数组customerGru[i]
记录不释放技能时前i
满意的顾客数,利用customerSum[i]
记录前i
天总的顾客数。利用一个长度为minutes
的滑动窗口向右移动,这个窗口内的几天老板都不会生气,所以让这些天内来的顾客都满意,所以用customerSum[j] - customerSum[i - 1]
代替customerGru[j] - customerGru[i - 1]
作为i~j
天内满意的顾客数
解法1:
利用数组记录前缀和
class Solution {
#define maxn 20005
public:
int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
int n = customers.size();
int customerGru[maxn];
int customerSum[maxn];
for (int i = 1; i <= n; i++) {
int index = i - 1;
customerGru[i] = customerGru[index] + (!grumpy[index]) * customers[index];
customerSum[i] = customerSum[index] + customers[index];
}
int i = 1, j = 0;
int ans = 0, temp;
while (j < n) {
j ++;
if (j - i + 1 > minutes) i++;
if (j - i + 1 == minutes) {
temp = customerGru[n] - (customerGru[j] - customerGru[i - 1]) + customerSum[j] - customerSum[i - 1];
ans = max(ans, temp);
}
}
return ans;
}
};
解法2:
修改原数组,不利用前缀和更新答案。利用变量value
获取长度为minutes
的窗口中,最大的由技能额外增加的满意的顾客数。最后答案为不放技能时满意的顾客数 + 放技能最多额外增加的满意的顾客数
class Solution {
#define maxn 20005
public:
int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
int n = customers.size();
int ans = 0;
for (int i = 0; i < n; i++) {
if (!grumpy[i]) {
ans += customers[i];
customers[i] = 0; // 当天的顾客已经全部满意,不需要在更新
}
}
int i = 0, j = -1;
int value = 0, sum_minutes = 0;
while (j < n - 1) {
j++;
if (j < minutes) {
sum_minutes += customers[j]; // 处理窗口小于技能时长的部分
value += customers[j];
}
if (j - i + 1 > minutes) i++;
if (i != 0 && j - i + 1 == minutes) {
//printf("j=%d ans=%d ans_new=%d\n", j, ans, ans - customers[j - minutes] + customers[j]);
sum_minutes = sum_minutes - customers[i - 1] + customers[j];
value = max(value, sum_minutes);
}
}
return ans + value;
}
};
东方欲晓,莫道君行早。