加载中...
1004-最大连续1的个数 III(Max Consecutive Ones III)
发表于:2021-12-03 | 分类: 中等
字数统计: 2.1k | 阅读时长: 8分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/max-consecutive-ones-iii

英文原文

Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's.

 

Example 1:

Input: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
Output: 6
Explanation: [1,1,1,0,0,1,1,1,1,1,1]
Bolded numbers were flipped from 0 to 1. The longest subarray is underlined.

Example 2:

Input: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], k = 3
Output: 10
Explanation: [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
Bolded numbers were flipped from 0 to 1. The longest subarray is underlined.

 

Constraints:

  • 1 <= nums.length <= 105
  • nums[i] is either 0 or 1.
  • 0 <= k <= nums.length

中文题目

给定一个由若干 01 组成的数组 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. 1 <= A.length <= 20000
  2. 0 <= K <= A.length
  3. A[i] 为 0 或 1 

通过代码

高赞题解

各位题友大家好! 今天是 @负雪明烛 坚持日更的第 26 天。今天力扣上的每日一题是「1004. 最大连续1的个数 III」。

解题思路

  • 重点:题意转换。把「最多可以把 K 个 0 变成 1,求仅包含 1 的最长子数组的长度」转换为 「找出一个最长的子数组,该子数组内最多允许有 K 个 0 」。

经过上面的题意转换,我们可知本题是求最大连续子区间,可以使用滑动窗口方法。滑动窗口的限制条件是:窗口内最多有 K 个 0。

可以使用我多次分享的滑动窗口模板解决,模板在代码之后。

代码思路:

  • 使用 $left$ 和 $right$ 两个指针,分别指向滑动窗口的左右边界。
  • $right$ 主动右移:$right$ 指针每次移动一步。当 $A[right]$ 为 $0$,说明滑动窗口内增加了一个 $0$;
  • $left$ 被动右移:判断此时窗口内 $0$ 的个数,如果超过了 $K$,则 $left$ 指针被迫右移,直至窗口内的 $0$ 的个数小于等于 $K$ 为止。
  • 滑动窗口长度的最大值就是所求。

示例

A= [1,1,1,0,0,0,1,1,1,1,0], K = 2 为例,下面的动图演示了滑动窗口的两个指针的移动情况。

1004.gif

该动图对应的 PPT 在下面,可以点击逐步观看:

<1004.001.jpeg,1004.002.jpeg,1004.003.jpeg,1004.004.jpeg,1004.005.jpeg,1004.006.jpeg,1004.007.jpeg,1004.008.jpeg,1004.009.jpeg,1004.010.jpeg,1004.011.jpeg,1004.012.jpeg,1004.013.jpeg,1004.014.jpeg,1004.015.jpeg,1004.016.jpeg,1004.017.jpeg,1004.018.jpeg,1004.019.jpeg,1004.020.jpeg,1004.021.jpeg,1004.022.jpeg>

代码

提供 Python, C++, Java 三种代码可供阅读。

[]
class Solution(object): def longestOnes(self, A, K): N = len(A) res = 0 left, right = 0, 0 zeros = 0 while right < N: if A[right] == 0: zeros += 1 while zeros > K: if A[left] == 0: zeros -= 1 left += 1 res = max(res, right - left + 1) right += 1 return res
[]
class Solution { public: int longestOnes(vector<int>& A, int K) { int res = 0, zeros = 0, left = 0; for (int right = 0; right < A.size(); ++right) { if (A[right] == 0) ++zeros; while (zeros > K) { if (A[left++] == 0) --zeros; } res = max(res, right - left + 1); } return res; } };
[]
class Solution { public int longestOnes(int[] A, int K) { int N = A.length; int res = 0; int left = 0, right = 0; int zeros = 0; while (right < N) { if (A[right] == 0) zeros ++; while (zeros > K) { if (A[left++] == 0) zeros --; } res = Math.max(res, right - left + 1); right ++; } return res; } }
  • 时间复杂度:$O(N)$,因为每个元素只遍历了一次。
  • 空间复杂度:$O(1)$,因为使用了常数个空间。

分享滑动窗口模板

《挑战程序设计竞赛》这本书中把滑动窗口叫做「虫取法」,我觉得非常生动形象。因为滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:前脚不动,把后脚移动过来;后脚不动,把前脚向前移动

我分享一个滑动窗口的模板,能解决大多数的滑动窗口问题:

def findSubArray(nums):
    N = len(nums) # 数组/字符串长度
    left, right = 0, 0 # 双指针,表示当前遍历的区间[left, right],闭区间
    sums = 0 # 用于统计 子数组/子区间 是否有效,根据题目可能会改成求和/计数
    res = 0 # 保存最大的满足题目要求的 子数组/子串 长度
    while right < N: # 当右边的指针没有搜索到 数组/字符串 的结尾
        sums += nums[right] # 增加当前右边指针的数字/字符的求和/计数
        while 区间[left, right]不符合题意:# 此时需要一直移动左指针,直至找到一个符合题意的区间
            sums -= nums[left] # 移动左指针前需要从counter中减少left位置字符的求和/计数
            left += 1 # 真正的移动左指针,注意不能跟上面一行代码写反
        # 到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
        res = max(res, right - left + 1) # 需要更新结果
        right += 1 # 移动右指针,去探索新的区间
    return res

滑动窗口中用到了左右两个指针,它们移动的思路是:以右指针作为驱动,拖着左指针向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。

模板的整体思想是:

  1. 定义两个指针 leftright 分别指向区间的开头和结尾,注意是闭区间;定义 sums 用来统计该区间内的各个字符出现次数;
  2. 第一重 while 循环是为了判断 right 指针的位置是否超出了数组边界;当 right 每次到了新位置,需要增加 right 指针的求和/计数;
  3. 第二重 while 循环是让 left 指针向右移动到 [left, right] 区间符合题意的位置;当 left 每次移动到了新位置,需要减少 left 指针的求和/计数;
  4. 在第二重 while 循环之后,成功找到了一个符合题意的 [left, right] 区间,题目要求最大的区间长度,因此更新 resmax(res, 当前区间的长度)
  5. right 指针每次向右移动一步,开始探索新的区间。

模板中的 sums 需要根据题目意思具体去修改,本题是求和题目因此把sums 定义成整数用于求和;如果是计数题目,就需要改成字典用于计数。当左右指针发生变化的时候,都需要更新 sums

另外一个需要根据题目去修改的是内层 while 循环的判断条件,即: 区间 $[left, right]$ 不符合题意 。对于本题而言,就是该区间内的 0 的个数 超过了 2 。


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、模拟面试、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

祝大家牛年大吉!AC 多多,Offer 多多!我们明天再见!

统计信息

通过次数 提交次数 AC比率
71396 119912 59.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
至多包含 K 个不同字符的最长子串 中等
替换后的最长重复字符 中等
最大连续 1 的个数 简单
最大连续1的个数 II 中等
上一篇:
1002-查找共用字符(Find Common Characters)
下一篇:
1006-笨阶乘(Clumsy Factorial)
本文目录
本文目录