487. 最大连续1的个数 II
给定一个二进制数组,你可以最多将 1 个 0 翻转为 1,找出其中最大连续 1 的个数。
示例 1:
输入:[1,0,1,1,0]
输出:4
解释:翻转第一个 0 可以得到最长的连续 1。
当翻转以后,最大连续 1 的个数为 4。
注:
- 输入数组只包含
0
和1
. - 输入数组的长度为正整数,且不超过 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 i−1。
状态转移
- 当前第 i i i 个位置仍没有使用操作,说明前 i − 1 i - 1 i−1 个位置都没有使用操作,所以只能由 f ( i − 1 , 0 ) f(i-1,0) f(i−1,0) 转移
- 当前第
i
i
i 个位置已经使用了操作,则有两种转移方式
- 前 i − 1 i - 1 i−1 个位置已经使用了操作,即从 f ( i − 1 , 1 ) f(i-1,1) f(i−1,1) 转移
- 前 i − 1 i - 1 i−1 个位置没使用但是第 i i i 个位置使用了操作,即从 f ( i − 1 , 0 ) f(i-1,0) f(i−1,0) 转移
状态计算
-
n
u
m
s
(
i
−
1
)
=
0
nums(i-1) = 0
nums(i−1)=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(i−1,0)+1
-
n
u
m
s
(
i
−
1
)
=
1
nums(i-1)=1
nums(i−1)=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(i−1,0)+1
- f ( i , 0 ) = f ( i − 1 , 1 ) + 1 f(i,0) = f(i-1,1)+1 f(i,0)=f(i−1,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 i−1 层有关,所以可以使用两组变量交替滚动更新。
复杂度
时间复杂度: 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;
}
}