最大连续1的个数

1.题目

给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。

返回仅包含 1 的最长(连续)子数组的长度。

示例 1:

输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:

输入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

提示:

1 <= A.length <= 20000
0 <= K <= A.length
A[i] 为 0 或 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-consecutive-ones-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.双指针解法,时间复杂度O(n)

这个是我自己的解法,因为原来做过类似的,直接就想到了这个方法,具体详情可以看我之前的博客,地址在这里

2.1 原理

我们设置两个指针,左指针和右指针,先让右指针移动,每经过一次0,我们就让记录值k - 1,一旦k == 0 时,就代表可替换的次数用完了,right - left 就是我们要的长度,那么后续的数该怎么办呢,这时候就要轮到我们左指针了,右指针继续移动,一旦k < 0,左指针开始移动,如果左边移过去的元素是0,我们就要让k++(此时我们的范围不包括这个0,所以可替换次数要恢复),这样不管如何,我们right - left 记录的始终是最大的长度。在前面的博客中,也是利用的这个思路,主要是弄懂为什么right - left 是最大长度。

2.2 代码

int longestOnes(vector<int>& A, int K) {
	int left = 0, right = 0;
	int k = K;
	while(right < A.size()) {
		if((A[right++] == 0))
			k--;
		if(k < 0) {
			if(A[left] == 0)
				k++;
			left++;
		}
	}
	return right - left;
}

3.官方解法总思路(4,5共同的思路部分)

来源于这里

前缀和:前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。

具体关于前缀和的可以看这里

我们可以转换一下题意,其实是让我们寻找一个包含K个0元素的最大子数组,我们将原数组的0 和 1,变成 1 和 0,这样我们就可以统计出哪个区段有多少个零元素,然后开辟一个数组存储前缀和,让右侧减去左侧的前一项,就是这个区段具有的0元素的个数。

4.二分查找法,时间复杂度O(nlogn)

4.1 原理

我们知道,我们得到的前缀和数组一定是递增的,因为数组只包含0 和 1,这样我们就可以利用二分法来找到符合我们要求的位置,我们让right从左端开始,在每一次循环中进行二分查找,查找到left的位置,然后再与最大的长度比较,决定是否取代之前的最大长度。

4.2 代码

class Solution {
public:
    int longestOnes(vector<int>& A, int K) {
        int n = A.size();
        vector<int> P(n + 1);
        for (int i = 1; i <= n; ++i) {
            P[i] = P[i - 1] + (1 - A[i - 1]);
        }

        int ans = 0;
        for (int right = 0; right < n; ++right) {
            int left = lower_bound(P.begin(), P.end(), P[right + 1] - K) - P.begin();
            ans = max(ans, right - left + 1);
        }
        return ans;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/max-consecutive-ones-iii/solution/zui-da-lian-xu-1de-ge-shu-iii-by-leetcod-hw12/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5.滑动窗口,时间复杂度O(n)

5.1 原理

我们知道前缀和是递增的,我们设置两个指针,左指针和右指针,先让右指针移动,我们无需再开辟数组来记录此时的前缀和值,我们只需要利用left和right两个下标再根据lsum和rsum这两个值来记录前缀和值,一旦不符合公式(根据题意推出来的公式),就让左指针开始移动,即窗口缩小,直到缩到符合要求为止,再与我们记录的最大长度去比较即可。

5.2 代码

class Solution {
public:
    int longestOnes(vector<int>& A, int K) {
        int n = A.size();
        int left = 0, lsum = 0, rsum = 0;
        int ans = 0;
        for (int right = 0; right < n; ++right) {
            rsum += 1 - A[right];
            while (lsum < rsum - K) {
                lsum += 1 - A[left];
                ++left;
            }
            ans = max(ans, right - left + 1);
        }
        return ans;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/max-consecutive-ones-iii/solution/zui-da-lian-xu-1de-ge-shu-iii-by-leetcod-hw12/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2021-02-19 21:59  爱敲代码的自闭杭  阅读(260)  评论(0)    收藏  举报