六月集训(第06天) —— 滑动窗口
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了六月集训(第06天) —— 滑动窗口相关的知识,希望对你有一定的参考价值。
前言
此为《英雄算法联盟:算法集训》的内容,具体内容详见:知识星球:英雄算法联盟 - 六月集训。加入星球后,即可享用星主 CSDN付费专栏 免费阅读 的权益。
欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
希望大家先自己思考,如果实在没有想法,再看下面的算法思路,如果有思路但是写不出来,可以参考朋友圈中其他人的代码,总有一款是适合你的,关注一下他,取其之长,补给之短。
今天集训的内容是:滑动窗口
今天的题前三题是滑动窗口的经典题,基本属于套模板。第四题,利用差分的思想,巧妙的化解第二层循环,代码较短,思考维度较深,建议好好想一下,实在想不出来,睡觉前再想想。
一、练习题目
题目链接 | 难度 |
---|---|
1984. 学生分数的最小差值 | ★☆☆☆☆ |
1763. 最长的美好子字符串 | ★★☆☆☆ |
2269. 找到一个数字的 K 美丽值 | ★★☆☆☆ |
995. K 连续位的最小翻转次数 | ★★★★☆ |
二、算法思路
1、学生分数的最小差值
(1)设定两个指针 i 和 j 分别代表窗口的左右区间;
(2)一开始是一个空窗口,所以 i = 0, j = -1;
(3)不断滑动窗口的右区间的值,当窗口长度大于 k 时,滑动左区间的值;
(4)当窗口长度为 k 时,统计最大值和最小值的差值,取所有的窗口差值中的最小值就是我们要求的答案了。
class Solution
public:
int minimumDifference(vector<int>& nums, int k)
sort(nums.begin(), nums.end());
int i = 0, j = -1;
int size = nums.size();
int ans = 1000000000;
while(j < size - 1)
++j;
while(j - i + 1 > k)
++i;
if(j - i + 1 == k)
ans = min(ans, nums[j] - nums[i]);
return ans;
;
2、最长的美好子字符串
(1)要求最长,所以我们可以从大到小枚举长度 l;
(2)然后利用第一题的滑动窗口,求出一个满足条件的解就是我们要求的答案了。
class Solution
int code(char c)
if( c >= 'a' && c <= 'z')
return c - 'a';
return c - 'A' + 26;
public:
string longestNiceSubstring(string s)
int l, i, j, k;
int size;
int hash[52];
for(l = s.size(); l > 0; --l)
i = 0;
j = - 1;
size = s.size();
memset(hash, 0, sizeof(hash));
while(j < size - 1)
++j;
++hash[ code(s[j]) ];
while(j - i + 1 > l)
--hash[ code(s[i]) ];
++i;
if(j - i + 1 == l)
for(k = 0; k < 26; ++k)
if( hash[k] && !hash[k+26] )
break;
else if( !hash[k] && hash[k+26] )
break;
if(k == 26)
return s.substr(i, j - i + 1);
return "";
;
3、找到一个数字的 K 美丽值
(1)这个题由于数组范围非常小,转换成字符串也就 9 个字符,所以不用滑动窗口也可以直接模拟过掉,如果想练习滑动窗口也可以用滑动窗口来做。
class Solution
public:
int divisorSubstrings(int num, int k)
int f[10];
f[0] = 1;
for(int i = 1; i < 10; ++i)
f[i] = f[i-1] * 10;
vector <int> stk;
int N = num;
int ret = 0;
while(num)
stk.push_back(num % 10);
num /= 10;
reverse(stk.begin(), stk.end());
int i = 0, j = -1;
int size = stk.size();
int s = 0;
while(j < size - 1)
++j;
s = s * 10 + stk[j];
while(j - i + 1 > k)
++i;
s %= f[k];
if(j - i + 1 == k)
if(s && N % s == 0)
++ret;
return ret;
;
4、K 连续位的最小翻转次数
(1)如果数据量较小,比如长度为 1000 的情况下,只需要从左往右,找到第一个为 1 的位置,将它后续 k 个位置都进行翻转。顺便修改对应的位置上的值,0变1,1变0,直到后面少于 k 个时,返回 -1;如果正好能够翻转完毕,则返回翻转次数。这个次数一定是最小的。时间复杂度为 O(nk)。这是一个贪心的思想。
(2)那么如果我们还是采用这种思想,可以简化翻转这一步操作将它降为 O(1)。
(3)我们需要实现以下几个接口:
(3.1)获取第 i 位的值;
(3.2)从 i 开始的 k 位执行翻转操作;
(4)首先来看翻转操作,用一个长度为 n 的数组 a,记录当前这个位置的操作次数,比如从 i 开始的 k 位执行翻转操作,其实就是对于两个点执行异或,哪两个点呢? a[i] 和 a[i+k] 这两个位置(第 i 个位置加一,第 i+k 个位置减一,由于只需要讨论奇偶性,所以直接采用异或即可)。
(5)获取第 i 个位置,其实就是求 (nums[i] + a[0] + … + a[i]) & 1,这一步可以通过一次扫描求出来。所以只需要一次扫描就可以把整个贪心的过程给计算出来了。
class Solution
int a[100010];
public:
int minKBitFlips(vector<int>& nums, int k)
memset(a, 0, sizeof(a));
int ans = 0, sum = 0;
for(int i = 0; i < nums.size(); ++i)
sum ^= a[i];
sum &= 1;
if( ( (sum + nums[i]) & 1) == 0 )
if(i + k > nums.size())
return -1;
a[i] ^= 1;
a[i+k] ^= 1;
++ans;
sum ^= 1;
return ans;
;
以上是关于六月集训(第06天) —— 滑动窗口的主要内容,如果未能解决你的问题,请参考以下文章