题目1:1004.最大连续1的个数 III
思路分析:
如果我们根据题干意思来做,每次寻找并翻转k个0的话,难度还是比较大,很复杂。我们不妨使用zero计数器来控制0的数量,控制在
思路1:暴力枚举+zero计数器
思路2:滑动窗口+zero计数器
本题滑动窗口分析:
1. 进窗口: 当 nums[right]!=0或者zero小于k,就进窗口,执行 right++。意思就是right++就代表符合题意。
2. 判断: 主要目的是更新 left到符合题干的位置,即: 减去一个零,使得zero计数器为k的位置。更新到位置也就完成了 出窗口。
3. 更新结果: ret是满足一个就更新一次,进窗口就是增加,出窗口就是减小(所以要和之前的比对,取最大)。
代码实现:
class Solution { public: int longestOnes(vector<int>& nums, int k) { int left=0,right=0,n=nums.size(); int zero=0,ret=0; while(right<n) { if(nums[right]==0) zero++; //zero计数器 while(zero>k) //出窗口 if(nums[left++]==0) zero--; ret=max(ret,right-left+1);//更新结果 right++; //符合要求进窗口-->right++; } return ret; } };
题目2:1658. 将x减到0的最小操作数
思路分析:
一会左删一会右删,让删除的总数等于x,这道题我们直接做会很难。
不妨正难则反:中间的部分的和一直是:sum-x,要求删除最少,那就是中间长度最长。这样题目要求就变成了:找子数组的和等于target=sum-x的最长子数组。
思路1:暴力枚举
思路2:滑动窗口O(N)
本题滑动窗口分析:
1. 进窗口: 维护数据 sum1, right++进窗口。
2. 判断: 如果 sum1>target,则需要出窗口来减少 sum1。出窗口操作: sum1-=nums[left++];
3. 更新结果: 需要满足条件再更新结果: if(sum1==target) ret=max(ret,right-left+1);
代码实现:
class Solution { public: int minOperations(vector<int>& nums, int x) { int left=0,right=0,n=nums.size(); int sum=0,sum1=0,ret=-1;、 //求和 for(int i=0;i<n;i++) sum += nums[i]; int target=sum-x; //细节处理: if(target<0) return -1; while(right<n) { sum1+=nums[right]; //进窗口 while(sum1>target) //判断 sum1-=nums[left++]; //出窗口 if(sum1==target) ret=max(ret,right-left+1); //更新结果 right++; } return (ret==-1?ret:n-ret); } };
收获满满✨:
- 正难则反,这个往往是最难的,需要多多体会。
- 体会进窗口和出窗口,理解方式多样。