题目描述
给定一个二进制数组 nums
和一个整数 k
。
k位翻转 就是从 nums
中选择一个长度为 k
的 子数组 ,同时把子数组中的每一个 0
都改成 1
,把子数组中的每一个 1
都改成 0
。
返回数组中不存在 0
所需的最小 k位翻转 次数。如果不可能,则返回 -1
。
子数组 是数组的 连续 部分。
示例 1:
输入:nums = [0,1,0], K = 1
输出:2
解释:先翻转 A[0],然后翻转 A[2]。
示例 2:
输入:nums = [1,1,0], K = 2
输出:-1
解释:无论我们怎样翻转大小为 2 的子数组,我们都不能使数组变为 [1,1,1]。
示例 3:
输入:nums = [0,0,0,1,0,1,1,0], K = 3
输出:3
解释:
翻转 A[0],A[1],A[2]: A变成 [1,1,1,1,0,1,1,0]
翻转 A[4],A[5],A[6]: A变成 [1,1,1,1,1,0,0,0]
翻转 A[5],A[6],A[7]: A变成 [1,1,1,1,1,1,1,1]
提示:
1 <= nums.length <= 105
1 <= k <= nums.length
解法
方法一:差分数组
我们注意到,对于若干个连续的元素进行反转,其结果与对这些元素进行反转的次序无关。因此我们可以贪心地考虑每个位置需要进行反转的次数。
我们不妨从左到右处理数组。
假设当前我们需要处理位置 $i$,而位置 $i$ 左侧的元素已经被处理完毕,如果 $i$ 位置的元素为 $0$,那么我们必须进行反转操作,我们需要将 $[i,..i+k-1]$ 区间内的元素进行反转。这里我们用一个差分数组 $d$ 来维护每个位置的反转次数,那么判断当前位置 $i$ 是否需要反转,只需要看 $s = \sum_{j=0}^{i}d[j]$ 以及 $nums[i]$ 的奇偶性,如果 $s$ 与 $nums[i]$ 奇偶性相同,那么位置 $i$ 的元素仍然为 $0$,需要进行反转。此时我们判断一下 $i+k$ 是否超出了数组的长度,如果超出了数组的长度,那么就无法完成目标,返回 $-1$。否则我们令 $d[i]$ 增加 $1$,同时令 $d[i+k]$ 减少 $1$,然后将答案增加 $1$,并且 $s$ 增加 $1$。
这样当我们处理完数组中的所有元素时,返回答案即可。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。这里 $n$ 是数组 $nums$ 的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class Solution:
def minKBitFlips(self, nums: List[int], k: int) -> int:
n = len(nums)
d = [0] * (n + 1)
ans = s = 0
for i, x in enumerate(nums):
s += d[i]
if s % 2 == x:
if i + k > n:
return -1
d[i] += 1
d[i + k] -= 1
s += 1
ans += 1
return ans
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Solution {
public int minKBitFlips(int[] nums, int k) {
int n = nums.length;
int[] d = new int[n + 1];
int ans = 0, s = 0;
for (int i = 0; i < n; ++i) {
s += d[i];
if (s % 2 == nums[i]) {
if (i + k > n) {
return -1;
}
++d[i];
--d[i + k];
++s;
++ans;
}
}
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution {
public:
int minKBitFlips(vector<int>& nums, int k) {
int n = nums.size();
int d[n + 1];
memset(d, 0, sizeof(d));
int ans = 0, s = 0;
for (int i = 0; i < n; ++i) {
s += d[i];
if (s % 2 == nums[i]) {
if (i + k > n) {
return -1;
}
++d[i];
--d[i + k];
++s;
++ans;
}
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | func minKBitFlips(nums []int, k int) (ans int) {
n := len(nums)
d := make([]int, n+1)
s := 0
for i, x := range nums {
s += d[i]
if s%2 == x {
if i+k > n {
return -1
}
d[i]++
d[i+k]--
s++
ans++
}
}
return
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | function minKBitFlips(nums: number[], k: number): number {
const n = nums.length;
const d: number[] = Array(n + 1).fill(0);
let [ans, s] = [0, 0];
for (let i = 0; i < n; ++i) {
s += d[i];
if (s % 2 === nums[i]) {
if (i + k > n) {
return -1;
}
d[i]++;
d[i + k]--;
s++;
ans++;
}
}
return ans;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | impl Solution {
pub fn min_k_bit_flips(nums: Vec<i32>, k: i32) -> i32 {
let n = nums.len();
let mut d = vec![0; n + 1];
let mut ans = 0;
let mut s = 0;
for i in 0..n {
s += d[i];
if s % 2 == nums[i] {
if i + (k as usize) > n {
return -1;
}
d[i] += 1;
d[i + (k as usize)] -= 1;
s += 1;
ans += 1;
}
}
ans
}
}
|
方法二:滑动窗口
我们可以用一个变量 $\textit{flipped}$ 来表示当前位置是否翻转,如果 $\textit{flipped}$ 为 $1$,表示当前位置已经翻转,否则表示当前位置未翻转。对于翻转过的位置,我们可以将其值设置为 $-1$,这样我们就可以区分出哪些位置已经翻转过了。
接下来我们从左到右遍历数组,对于每个位置 $i$,如果 $i \geq k$ 且 $i-k$ 位置的元素为 $-1$,那么当前位置的翻转状态应该与前一个位置的翻转状态相反。即 $\textit{flipped} = \textit{flipped} \oplus 1$。如果当前位置的元素与当前位置的翻转状态相同,那么我们需要翻转当前位置,此时我们判断一下 $i+k$ 是否超出了数组的长度,如果超出了数组的长度,那么就无法完成目标,返回 $-1$。否则我们将当前位置的翻转状态取反,同时将答案增加 $1$,并且将当前位置的元素设置为 $-1$。
这样当我们处理完数组中的所有元素时,返回答案即可。
时间复杂度 $O(n)$,其中 $n$ 是数组 $nums$ 的长度。空间复杂度 $O(1)$。
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Solution:
def minKBitFlips(self, nums: List[int], k: int) -> int:
ans = flipped = 0
for i, x in enumerate(nums):
if i >= k and nums[i - k] == -1:
flipped ^= 1
if x == flipped:
if i + k > len(nums):
return -1
flipped ^= 1
ans += 1
nums[i] = -1
return ans
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Solution {
public int minKBitFlips(int[] nums, int k) {
int n = nums.length;
int ans = 0, flipped = 0;
for (int i = 0; i < n; ++i) {
if (i >= k && nums[i - k] == -1) {
flipped ^= 1;
}
if (flipped == nums[i]) {
if (i + k > n) {
return -1;
}
flipped ^= 1;
++ans;
nums[i] = -1;
}
}
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | class Solution {
public:
int minKBitFlips(vector<int>& nums, int k) {
int n = nums.size();
int ans = 0, flipped = 0;
for (int i = 0; i < n; ++i) {
if (i >= k && nums[i - k] == -1) {
flipped ^= 1;
}
if (flipped == nums[i]) {
if (i + k > n) {
return -1;
}
flipped ^= 1;
++ans;
nums[i] = -1;
}
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | func minKBitFlips(nums []int, k int) (ans int) {
flipped := 0
for i, x := range nums {
if i >= k && nums[i-k] == -1 {
flipped ^= 1
}
if flipped == x {
if i+k > len(nums) {
return -1
}
flipped ^= 1
ans++
nums[i] = -1
}
}
return
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | function minKBitFlips(nums: number[], k: number): number {
const n = nums.length;
let [ans, flipped] = [0, 0];
for (let i = 0; i < n; i++) {
if (nums[i - k] === -1) {
flipped ^= 1;
}
if (nums[i] === flipped) {
if (i + k > n) {
return -1;
}
flipped ^= 1;
++ans;
nums[i] = -1;
}
}
return ans;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | impl Solution {
pub fn min_k_bit_flips(mut nums: Vec<i32>, k: i32) -> i32 {
let mut ans = 0;
let mut flipped = 0;
let k = k as usize;
for i in 0..nums.len() {
if i >= k && nums[i - k] == -1 {
flipped ^= 1;
}
if flipped == nums[i] {
if i + k > nums.len() {
return -1;
}
flipped ^= 1;
ans += 1;
nums[i] = -1;
}
}
ans
}
}
|