Leetcode 995. Minimum Number of K Consecutive Bit Flips

原题

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

分析

在给出一个0, 1序列后,可以对其中连续K个数字进行翻转(0翻转为1,1翻转为0)。如果想要全部翻转为1,那么直观的一个感受就是从左到右,如果当前位置的数字是0,那么可以作为需要翻转的连续K个数字的起点,因此最简单的思路:

  1. 从左到右,寻找值为0的下标index
  2. 对A[index, index + K-1]的数字进行翻转
  3. 重复1, 2,直至数组值全为1,或者index + K > A.length为止

拍脑门解法

Note:需要注意的一点是,在进行翻转的时候所采取的方法决定是否超时,用^进行异或操作会减少用时,特别地,与1异或表示对当前位进行翻转

Accept代码

class Solution {
public:
	bool flip(vector<int> & A, int K, int pos) {
		if (pos + K > A.size())
			return false;
		for (int i = pos; i < pos + K; ++i) {
			A[i] ^= 1;
		}
		return true;
		
	}

	int minKBitFlips(vector<int>& A, int K) {
		int n = A.size();
		int ans = 0, last_pos = 0; 
		
		for (int i = 0; i < n; ++i) {
			if (A[i] == 0) {
				ans++;
				if (!flip(A, K, i))
					return -1;
			}
		}
		return ans;

	}
};

在上面的解法中,之前用if else语句对A数组值进行翻转,结果超时

bool flip(vector<int> & A, int K, int pos) {
		if (pos + K > A.size())
			return false;
		for (int i = pos; i < pos + K; ++i) {
			//超时代码
			if(A[i] == 1)
				A[i] = 0;
			else 
				A[i] = 1;
		}
		return true;
		
	}

这个方法速度很慢,用时5216 ms,在超时的边缘徘徊,于是借鉴别人的解法。

他山之石,可以攻玉(别人的答案)

在提交之后可以看到别人提交的答案,看完后再看看自己的代码便更觉得羞涩难挡,因此去掉了贴自己代码这一步,转而分析别人代码。

作者@awice

思路:
之前的贪心算法虽然可以成功运行,但是耗时较长(主要耗时的部分在翻转A[index, index+K-1]部分,当次翻转范围与上次翻转范围存在重叠,当重叠部分进行翻转的两次的时候,相当于没有翻转,造成了时间的浪费)。因此用一个flip位来记录当前K个连续数字是否已经翻转,用一个hint数组来记录翻转。

算法流程:

  1. 初始化flip =0, hint数组为0

  2. 遍历数组A,

  3. flip ^= hint[i], 异或运算后flip位表示当前位置i是否被翻转过,flip=0,未翻转,flip=1,翻转。hint数组用来记录翻转,(特别地,当A[i]需要翻转时,hint[i + K]记录翻转截止范围,见下面第4步骤)

  4. 判断A[i] == flip,

    • A[i]与flip均为0时,表示A[i] = 0且当前位未被翻转,需要进行翻转
    • A[i]与flip均为1时,表示A[i] = 1且当前位已被翻转(实际值为0),需要再次翻转

    若A[i] == flip, 表明需要进行翻转

    • 翻转次数加一,ans++
    • 判断i+K是否超出A数组长度,超出则失败返回-1
    • 对flip位进行翻转
    • 对hint[i+K]位进行翻转
class Solution {
public:
	int minKBitFlips(vector<int>& A, int K) {
		int n = A.size();
		vector<int> hint(n, 0);
		int ans = 0, flip = 0;

		for (int i = 0; i < n; ++i) {
			flip ^= hint[i];

			if (A[i] == flip) {
				ans++;
				if (i + K > n)
					return -1;
				flip ^= 1;
				if (i + K < n)
					hint[i + K] ^= 1;
			}
		}
		return ans;
	}
};

使用改进算法对重叠的部分进行索引记录后,程序运行时间大大减少,为112ms,缩短了近50倍。果然还是别人的饭吃起来香~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值