给定一个二进制数组 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]
差分
cpp
class Solution {
public:
int minKBitFlips(vector<int>& nums, int k) {
int n = nums.size();
vector<int> diff(n+1);
int s = 0, ans = 0;
for(int i = 0; i < n; i++){
//s是偶数的时候,翻转后还是原来元素,奇数时候才有效果。
s += diff[i];
if((nums[i] + s) % 2 == 0){
s++;
ans++;
if(i+k > n){
return -1;
}
diff[i+k]--;
}
}
return ans;
}
};
这道题使用差分的思路是非常巧妙的方法。
首先我们定义差分的数组diff,整型s,s实际上就是某一个元素的翻转次数,整型ans用来储存总共翻转几次区间。
我们首先需要知道,比如当k=3的时候,我们翻转某一个元素,这时候会影响到这个元素及其后面两个元素。所以当翻转某一个元素的时候,我们就令s++
,然后diff[i+k]--
, 他由diff[i+k-1+1]
化简。这时候这个元素及后面k-1个元素都会记录被反转。
当某个元素翻转s次后,如果是偶数,说明他受前面元素的翻转影响后,会是0。要注意的是,我们在判断某一个元素是否需要主动翻转的时候,是根据他受前面被动的翻转后是否为0来判断的。
每次主动翻转就s++
,然后diff[i+k]
,从而影响这个元素及其后面的k-1个元素。
由于diff最多到diff(n)是合法的,所以当i+k>n
的时候就返回-1。