487. 最大连续1的个数 II

487. 最大连续1的个数 II

给定一个二进制数组,你可以最多将 1 个 0 翻转为 1,找出其中最大连续 1 的个数。

示例 1:

输入:[1,0,1,1,0]
输出:4
解释:翻转第一个 0 可以得到最长的连续 1。
     当翻转以后,最大连续 1 的个数为 4。

注:

  • 输入数组只包含 01.
  • 输入数组的长度为正整数,且不超过 10,000

解法一:经典 DP

思路

状态表示,操作 即由 0 0 0 1 1 1

  • f ( i , 0 ) f(i, 0) f(i,0) 表示包含第 i i i 个位置,没有使用操作,最大连续 1 1 1 的最大个数
  • f ( i , 1 ) f(i, 1) f(i,1) 表示包含第 i i i 个位置,已经使用操作,最大连续 1 1 1 的最大个数

此处 i i i 1 1 1 开始,题中 n u m s nums nums 的下标由 0 0 0 开始。也就是 f f f i i i 对应 n u m s nums nums i − 1 i - 1 i1

状态转移

  • 当前第 i i i 个位置仍没有使用操作,说明前 i − 1 i - 1 i1 个位置都没有使用操作,所以只能由 f ( i − 1 , 0 ) f(i-1,0) f(i1,0) 转移
  • 当前第 i i i 个位置已经使用了操作,则有两种转移方式
    • i − 1 i - 1 i1 个位置已经使用了操作,即从 f ( i − 1 , 1 ) f(i-1,1) f(i1,1) 转移
    • i − 1 i - 1 i1 个位置没使用但是第 i i i 个位置使用了操作,即从 f ( i − 1 , 0 ) f(i-1,0) f(i1,0) 转移

状态计算

  • n u m s ( i − 1 ) = 0 nums(i-1) = 0 nums(i1)=0
    • 不使用操作,连续的 1 中断,所以 f ( i , 0 ) = 0 f(i,0)=0 f(i,0)=0
    • 若当前使用操作,只能从前面没使用操作时转移, f ( i , 1 ) = f ( i − 1 , 0 ) + 1 f(i,1) = f(i-1,0) + 1 f(i,1)=f(i1,0)+1
  • n u m s ( i − 1 ) = 1 nums(i-1)=1 nums(i1)=1 ,连续的 1 1 1 不会中断所以全部直接加 1 1 1
    • f ( i , 0 ) = f ( i − 1 , 0 ) + 1 f(i,0) = f(i-1,0)+1 f(i,0)=f(i1,0)+1
    • f ( i , 0 ) = f ( i − 1 , 1 ) + 1 f(i,0) = f(i-1,1)+1 f(i,0)=f(i1,1)+1

一个小贪心,使用了操作肯定比没使用操作的连续的 1 1 1 长,所以取最大长度时只要取使用了操作中的最大值。

复杂度

时间复杂度: O ( N ) O(N) O(N)

空间复杂度: O ( N ) O(N) O(N)

代码

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int n = nums.length;
        int[][] f = new int[n + 1][2];
        int res = 0;
        for (int i = 1; i <= n; i++) {
            if (nums[i - 1] == 1) {
                f[i][0] = f[i - 1][0] + 1;
                f[i][1] = f[i - 1][1] + 1;
            } else {
                f[i][0] = 0;
                f[i][1] = f[i - 1][0] + 1;
            }
            res = Math.max(res, f[i][1]);
        }
        return res;

    }
}

解法二:滚动变量优化

思路

由解法一可得所有状态的递推公式。

f[i][0] = f[i - 1][0] + 1;
f[i][1] = f[i - 1][1] + 1; 
f[i][0] = 0;
f[i][1] = f[i - 1][0] + 1;

发现第 i i i 层只有第 i − 1 i - 1 i1 层有关,所以可以使用两组变量交替滚动更新。

复杂度

时间复杂度: O ( N ) O(N) O(N)

空间复杂度: O ( 1 ) O(1) O(1)

代码

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int n = nums.length;
        int res = 0;
        int a = 0, b = 0;
        for (int i = 1; i <= n; i++) {
            int x = a, y = b;
            if (nums[i - 1] == 1) {
                a = x + 1;
                b = y + 1;
            } else {
                a = 0;
                b = x + 1;
            }
            res = Math.max(res, b);
        }
        return res;

    }
}

解法三:滑动窗口

思想

窗口里 0 0 0 的个数小于等于 1 1 1 r r r 往右移,扩大整个窗口,每次更新 l l l ,算一下答案。

窗口里 0 0 0 的个数若大于 1 1 1 l l l 往右移,缩小整个窗口。

复杂度

时间复杂度: O ( N ) O(N) O(N)

空间复杂度: O ( 1 ) O(1) O(1)

代码

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int n = nums.length;
        int l = 0, r = 0, cnt = 0, res = 0;
        while (r < n) { 
            while (r < n && cnt <= 1) {
                if (nums[r] == 0) cnt++;
                r++;
                if (cnt <= 1) res = Math.max(r - l, res);
            }
            while (l <= r && cnt > 1) {
                if (nums[l] == 0) cnt--;
                l++;
            }
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值