1004.最大连续1的个数 |||
1004. 最大连续1的个数 III - 力扣(LeetCode)
题目解析:
这道题如果能转化为滑动窗口的话就会很简单,因为我们如果尝试去把0翻转为1再计数的话等到第2轮又得重新翻转回来,费时费力~
那么我们不妨默认就把0当作1,选中一段区间只要里面0的个数不超过k那就可以了。这样就把问题转化为求0的个数不超过k的最长子数组,妥妥用滑动窗口。
老规矩,我们先来用暴力找找灵感:
需要列举的有点多,不过也有很多可优化的空间~
算法解析:
其实当我们转换为滑动窗口后相信看过我前边博客的友友们肯定都知道了,所以我们废话不多说。
在right不断遍历的过程中遇到0就记录其个数,等到不满足条件(sum>k)时说明在它之前已经找到最长子数组了。开始新一轮的寻找,这时候我们的right不用再回来遍历,因为之前都已经遍历并记录在sum中了,所以留在原位移动left即可。
滑动窗口流程:
滑动窗口的两个指针移动规律也很简单:
- right 遇1——> right++
- right 遇0——> sum++,right++
- left 遇1——> left++
- left 遇0——> sum--,left++
代码:
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size();
int sum = 0;
int ret = INT_MIN;
for (int left = 0, right = 0; right < n; right++)
{
//扩充窗口
if (nums[right] == 0)
{
sum++;
}
//判断,若sum<=k,到下一循环继续扩充
//若sum>k,缩小窗口
while (sum > k)
{
if (nums[left] == 0)
{
sum--;
}
left++;
}
//更新长度
ret = max(ret, right - left + 1);
}
return ret;
}
};
1658.将x减到0的最小操作数
1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)
题目解析:
这道题从正面来做会很难,因为你要不断考虑是从左边选数还是从右边选数。那么我们不如从另外一个角度来思考这个问题~
首先我们要把通过移除左右两边数使得x==0转换为挑选出左右两区间使得里面的数之和为x.
但两段区间也还是麻烦,我们不妨去挑出处于两区间中间的连续区间。
我们只需要让这个连续区间里面的和为sum-x就可以了,再求出连续区间的长度len,最后n-len反推出真正的最小操作数。
算法解析:
问题转换:求出总数之和为sum-x的最长连续子数组
只要t<a,我们就让right继续往后遍历直到出现t>=a的情况。
而在第二轮开始遍历的时候right其实不用去复位,因为在t>=a的那个节点前面的总和本来就是t<a,left前进一位只会让t更小。所以right是没必要复位的,留在原地就好了。
滑动窗口流程:
最后有一点小细节:
如果题目给的x比我们所有数组之和还大,得特殊处理~
代码:
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int len = -1;
int n = nums.size();
int sum = 0;
for (int i : nums)
{
sum += i;
}
//遍历连续数组之和
int target = 0;
//连续段的期望数值
int a = sum - x;
//细节处理
if (a < 0) return -1;
for (int left = 0, right = 0; right < n; right++)
{
//扩充窗口
target += nums[right];
//判断,只要target>a就缩小窗口,反之就进行下一轮的扩充
while (target > a)
{
//缩小窗口
target -= nums[left++];
}
//只有达到期望数值才可以记录长度
if (target == a)
{
len = max(len, right - left + 1);
}
}
//没有达到期望数值,说明找不到
if (len == -1)
{
return -1;
}
else
{
return n - len;
}
}
};