In an array A containing only 0s and 1s, a K-bit flip consists of choosing a (contiguous) subarray of length K and simultaneously changing every 0 in the subarray to 1, and every 1 in the subarray to 0.
Return the minimum number of K-bit flips required so that there is no 0 in the array. If it is not possible, return -1.
Example 1:
Input: A = [0,1,0], K = 1
Output: 2
Explanation: Flip A[0], then flip A[2].
Example 2:
Input: A = [1,1,0], K = 2
Output: -1
Explanation: No matter how we flip subarrays of size 2, we can’t make the array become [1,1,1].
Example 3:
Input: A = [0,0,0,1,0,1,1,0], K = 3
Output: 3
Explanation:
Flip A[0],A[1],A[2]: A becomes [1,1,1,1,0,1,1,0]
Flip A[4],A[5],A[6]: A becomes [1,1,1,1,1,0,0,0]
Flip A[5],A[6],A[7]: A becomes [1,1,1,1,1,1,1,1]
Note:
1 <= A.length <= 30000
1 <= K <= A.length
题目大意:给定一个数组和一个整数K,数组中只包含0,1,每次操作可以将连续的K个数取反(0变1,1变0),问最少操作多少次能使得数组所有元素为1。
思路一:贪心算法,遇到0则翻转(证明过程参考),这种解法的时间复杂度为O(nK),这样当K很大时就会超时。
class Solution {
public:
int minKBitFlips(vector<int>& A, int K) {
int n = A.size();
int ans = 0;
for(int i = 0;i < n; i++){
if(A[i] == 0){
if(i > n-K)
return -1;
ans++;
for(int j = i;j < i + K; j++)
A[j] = 1 - A[j];
}
}
return ans;
}
};
思路二:上面的超时原因在于每次遇到0就不可避免地需要处理K个元素,这样导致复杂度为O(nK),如果能够想出一种每次翻转不用处理K个元素的方法,那就能将时间复杂度降到O(n)。怎么做?
每次翻转时,不修改后面的K个数,只标注一下在
i
i
i处进行了翻转操作(也就是将make_flip[i]置为1),在遍历数组的过程中使用一个变量fliped记录当前元素
A
[
i
]
A[i]
A[i]是否已经被翻转过,在上一次循环过程中该变量表示的是前一个元素
A
[
i
−
1
]
A[i-1]
A[i−1]是否被翻转过,已知上一个元素
A
[
i
−
1
]
A[i-1]
A[i−1]对应的fliped的值怎么得到当前元素
A
[
i
]
A[i]
A[i]对应的值?答案就是查看变量make_flip[i-K]的值,对于fliped值的更新如下四种情况:
A[i-1]对应的fliped的值 | is_fliped[i-K] | A[i]对应的fliped的值 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
当is_fliped[i-K]为1时,表示i-K处进行过翻转操作,这次翻转是将[i-K,i-1]这个区间内的元素都取反了,所以A[i-1]处的fliped的取值是受过该次翻转的影响的,因为该次翻转最远影响到i-1处,所以在计算A[i]处的fliped的取值时就应该去掉这个影响。当is_fliped[i-K]为0时,则A[i]对应的fliped的值等于A[i-1]对应的值。
计算出当前元素是否被翻转后就可以根据它判断当前位置是否需要进行翻转操作,因为我们的目的是将所有元素变为1,所以以上两种情况需要进行翻转操作:
- 原来取值为0且未被翻转过(A[i]=0,fliped=0)
- 原来取值为1且被翻转了(A[i]=1,fliped=1)
class Solution {
public:
int minKBitFlips(vector<int>& A, int K) {
int n = A.size();
int fliped = 0, ans = 0;
// 记录在i处是否执行翻转操作
vector<int> make_flip(n, 0);
for(int i = 0;i < n; i++){
if(i >= K){
fliped = fliped ^ make_flip[i-K];
}
if((A[i] == 0 && fliped == 0) || (A[i] == 1 && fliped == 1)){
if(i+K > n)
return -1;
ans++;
make_flip[i] = 1;
// 翻转过后更新fliped的值
fliped ^= 1;
}
}
return ans;
}
};
参考链接:
https://www.youtube.com/watch?v=bprYcBnlhhU&t=310s