995. Minimum Number of K Consecutive Bit Flips
Problem Description
You have a binary array nums
containing only 0s and 1s, and an integer k
.
A k-bit flip operation means:
- Select a contiguous subarray of exactly length
k
fromnums
- Flip all bits in that subarray (change all 0s to 1s and all 1s to 0s)
Your goal is to transform the array so that it contains only 1s (no 0s remain).
Return the minimum number of k-bit flip operations needed to achieve this. If it's impossible to eliminate all 0s, return -1
.
Example walkthrough:
- If
nums = [0,1,0]
andk = 1
, you can flip each 0 individually (2 flips total) - If
nums = [1,1,0]
andk = 2
, you can flip the subarray[1,0]
to get[1,0,1]
(1 flip total) - If
nums = [0,0,0,1,0,1,1,0]
andk = 3
, you need multiple flips to eliminate all 0s
The solution uses a greedy approach with a difference array to track flip effects. It processes the array from left to right, flipping whenever a 0 is encountered (accounting for previous flips' cumulative effects). The difference array d
efficiently tracks where flips start and end, while s
maintains the running count of active flips at each position. When s % 2 == nums[i]
, the current position needs a flip (since an even number of flips leaves the original value unchanged, and we need to flip 0s to 1s).
Intuition
The key insight is that flipping order doesn't matter - flipping subarray A then B gives the same result as flipping B then A. This means we can use a greedy approach.
Starting from the leftmost position, if we encounter a 0, we must flip it at some point. Since flips are expensive (we want minimum flips), we should flip as soon as we encounter a 0. Why? Because delaying the flip doesn't help - we'll still need to flip it eventually, and flipping early might help us fix multiple 0s in one operation.
But here's the challenge: when we flip a subarray, it affects k
consecutive elements. How do we efficiently track which positions have been affected by previous flips?
Consider position i
. It might have been affected by flips that started at positions i-k+1
, i-k+2
, ..., up to i
. If position i
has been flipped an odd number of times, a original 0 becomes 1 and original 1 becomes 0. If flipped an even number of times, it returns to its original value.
To track this efficiently, we use a difference array trick. When we flip at position i
:
- Mark the start of flip at position
i
with+1
- Mark the end of flip at position
i+k
with-1
As we scan through the array, we maintain a running sum s
of these markers. This s
tells us how many flips are "active" at the current position.
The clever part: if s % 2 == nums[i]
, then the effective value at position i
is 0:
- If
nums[i] = 0
ands
is even (even flips), the position remains 0 - If
nums[i] = 1
ands
is odd (odd flips), the position becomes 0
In both cases, we need to perform a flip starting at position i
. This greedy choice is optimal because we process left-to-right and this is the earliest opportunity to fix this 0.
Learn more about Queue, Prefix Sum and Sliding Window patterns.
Solution Approach
The implementation uses a difference array pattern to efficiently track the cumulative effect of flips at each position.
Data Structures:
d
: Difference array of sizen+1
to mark flip boundariess
: Running sum to track the number of active flips at current positionans
: Counter for total number of flips performed
Algorithm Steps:
-
Initialize variables:
- Create difference array
d
with all zeros - Set
ans = 0
(flip counter) ands = 0
(active flips counter)
- Create difference array
-
Process each position from left to right:
for i, x in enumerate(nums): s += d[i] # Update active flips count
- Add
d[i]
to running sums
to account for flips starting/ending at positioni
- Add
-
Check if current position needs flipping:
if s % 2 == x:
- If
s % 2 == x
, the effective value at positioni
is 0 - This happens when:
x = 0
ands
is even (no net flip effect)x = 1
ands
is odd (flipped to 0)
- If
-
Perform flip if needed:
if i + k > n: return -1 # Can't flip - would go out of bounds d[i] += 1 # Mark flip start d[i + k] -= 1 # Mark flip end s += 1 # Update active flips ans += 1 # Increment flip counter
- First check if flip is possible (won't exceed array bounds)
- Use difference array technique: increment at start position, decrement at end position
- Update
s
immediately since we're starting a new flip at current position - Increment answer counter
-
Return result:
- After processing all positions, return
ans
as the minimum flips needed
- After processing all positions, return
Why the difference array works:
- When we set
d[i] += 1
andd[i+k] -= 1
, we're marking that a flip affects positions[i, i+k-1]
- As we scan left to right with running sum
s
, it automatically tracks how many flips are active - This avoids the O(k) cost of updating each position in the flip range, making the solution O(n) overall
Time Complexity: O(n) - single pass through the array Space Complexity: O(n) - for the difference array
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through nums = [0,1,0]
with k = 2
:
Initial state:
nums = [0,1,0]
,n = 3
,k = 2
d = [0,0,0,0]
(difference array of size n+1)s = 0
(active flips),ans = 0
(total flips)
Step 1: Process index 0 (value = 0)
- Update active flips:
s = s + d[0] = 0 + 0 = 0
- Check if flip needed:
s % 2 == nums[0]
→0 == 0
✓ (position needs flip) - Can we flip?
i + k = 0 + 2 = 2 ≤ 3
✓ (within bounds) - Perform flip:
d[0] += 1
→d = [1,0,0,0]
(mark flip start)d[2] -= 1
→d = [1,0,-1,0]
(mark flip end)s += 1
→s = 1
(one flip now active)ans += 1
→ans = 1
- This flip affects positions [0,1], changing them from [0,1] to [1,0]
Step 2: Process index 1 (value = 1)
- Update active flips:
s = s + d[1] = 1 + 0 = 1
- Check if flip needed:
s % 2 == nums[1]
→1 == 1
✓ (position needs flip)- Original value was 1, but with 1 active flip it's now 0, so we need to flip
- Can we flip?
i + k = 1 + 2 = 3 ≤ 3
✓ (within bounds) - Perform flip:
d[1] += 1
→d = [1,1,-1,0]
(mark flip start)d[3] -= 1
→d = [1,1,-1,-1]
(mark flip end)s += 1
→s = 2
(two flips now active)ans += 1
→ans = 2
- This flip affects positions [1,2], changing them from [0,0] to [1,1]
Step 3: Process index 2 (value = 0)
- Update active flips:
s = s + d[2] = 2 + (-1) = 1
- The -1 indicates the first flip (from index 0) has ended
- Check if flip needed:
s % 2 == nums[2]
→1 == 0
✗ (position doesn't need flip)- Original value was 0, with 1 active flip it's now 1, which is what we want
Final Result:
- Return
ans = 2
- The array after all flips would be [1,1,1]
Verification of flips:
- First flip at index 0: [0,1,0] → [1,0,0]
- Second flip at index 1: [1,0,0] → [1,1,1] ✓
The difference array efficiently tracked when flips started and ended, allowing us to maintain the count of active flips at each position with just O(1) updates per flip.
Solution Implementation
1class Solution:
2 def minKBitFlips(self, nums: List[int], k: int) -> int:
3 n = len(nums)
4 # Difference array to track flip operations
5 # flip_diff[i] represents the change in flip count at position i
6 flip_diff = [0] * (n + 1)
7
8 # Total number of flips performed
9 total_flips = 0
10 # Current cumulative flip count affecting position i
11 current_flip_count = 0
12
13 for i, num in enumerate(nums):
14 # Update current flip count with the difference at position i
15 current_flip_count += flip_diff[i]
16
17 # Check if current position needs flipping
18 # If current_flip_count is even and num is 0, or
19 # if current_flip_count is odd and num is 1, we need to flip
20 if current_flip_count % 2 == num:
21 # Check if we can perform a k-length flip starting at i
22 if i + k > n:
23 # Cannot flip: would exceed array bounds
24 return -1
25
26 # Mark the start of flip at position i
27 flip_diff[i] += 1
28 # Mark the end of flip at position i + k
29 flip_diff[i + k] -= 1
30 # Update current flip count for this new flip
31 current_flip_count += 1
32 # Increment total number of flips
33 total_flips += 1
34
35 return total_flips
36
1class Solution {
2 public int minKBitFlips(int[] nums, int k) {
3 int n = nums.length;
4 // Difference array to track flip effects
5 // differenceArray[i] represents the change in flip count at position i
6 int[] differenceArray = new int[n + 1];
7 int flipCount = 0; // Total number of k-bit flips performed
8 int currentFlipEffect = 0; // Accumulated flip effect at current position
9
10 for (int i = 0; i < n; i++) {
11 // Update current flip effect by adding the difference at position i
12 currentFlipEffect += differenceArray[i];
13
14 // Check if current bit needs to be flipped
15 // If currentFlipEffect is even and nums[i] is 0, or
16 // if currentFlipEffect is odd and nums[i] is 1, we need to flip
17 if (currentFlipEffect % 2 == nums[i]) {
18 // Check if we can perform a k-bit flip starting at position i
19 if (i + k > n) {
20 // Cannot flip k bits starting from i (would go out of bounds)
21 return -1;
22 }
23
24 // Perform a k-bit flip starting at position i
25 // Mark the start of flip effect
26 differenceArray[i]++;
27 // Mark the end of flip effect (after k positions)
28 differenceArray[i + k]--;
29 // Update current flip effect immediately
30 currentFlipEffect++;
31 // Increment the total flip count
32 flipCount++;
33 }
34 }
35
36 return flipCount;
37 }
38}
39
1class Solution {
2public:
3 int minKBitFlips(vector<int>& nums, int k) {
4 int n = nums.size();
5 // Difference array to track flip operations
6 // diff[i] represents the change in flip count at position i
7 vector<int> diff(n + 1, 0);
8
9 int flipsCount = 0; // Total number of k-bit flips performed
10 int currentFlips = 0; // Number of active flips affecting current position
11
12 for (int i = 0; i < n; ++i) {
13 // Update current flip count based on difference array
14 currentFlips += diff[i];
15
16 // Check if current bit needs to be flipped
17 // If currentFlips is even and nums[i] is 0, or
18 // if currentFlips is odd and nums[i] is 1, we need to flip
19 if (currentFlips % 2 == nums[i]) {
20 // Check if we have enough space to perform a k-bit flip
21 if (i + k > n) {
22 return -1; // Cannot flip k bits starting from position i
23 }
24
25 // Mark the start of a new flip operation
26 diff[i]++;
27 // Mark the end of this flip operation (after k positions)
28 diff[i + k]--;
29
30 // Update current flip count for this position
31 currentFlips++;
32 // Increment total flips performed
33 flipsCount++;
34 }
35 }
36
37 return flipsCount;
38 }
39};
40
1/**
2 * Finds the minimum number of k-bit flips needed to make all elements in the array equal to 1
3 * @param nums - The binary array to be flipped
4 * @param k - The length of each flip operation
5 * @returns The minimum number of flips, or -1 if impossible
6 */
7function minKBitFlips(nums: number[], k: number): number {
8 const arrayLength: number = nums.length;
9
10 // Difference array to track flip effects
11 // differenceArray[i] represents the change in flip count at position i
12 const differenceArray: number[] = Array(arrayLength + 1).fill(0);
13
14 let flipsCount: number = 0; // Total number of flips performed
15 let currentFlipEffect: number = 0; // Cumulative flip effect at current position
16
17 for (let i = 0; i < arrayLength; i++) {
18 // Update current flip effect based on difference array
19 currentFlipEffect += differenceArray[i];
20
21 // Check if current position needs flipping
22 // If currentFlipEffect is even and nums[i] is 0, or
23 // if currentFlipEffect is odd and nums[i] is 1, we need to flip
24 if (currentFlipEffect % 2 === nums[i] % 2) {
25 // Check if flip would go out of bounds
26 if (i + k > arrayLength) {
27 return -1; // Impossible to flip
28 }
29
30 // Mark the start of flip at current position
31 differenceArray[i]++;
32
33 // Mark the end of flip effect at position i + k
34 differenceArray[i + k]--;
35
36 // Update current flip effect
37 currentFlipEffect++;
38
39 // Increment total flips count
40 flipsCount++;
41 }
42 }
43
44 return flipsCount;
45}
46
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through the array nums
exactly once using a single for loop from index 0 to n-1. Within each iteration, all operations performed are constant time:
- Accessing and updating the difference array
d[i]
:O(1)
- Updating the running sum
s
:O(1)
- Checking conditions and updating counters:
O(1)
Since we perform O(1)
operations for each of the n
elements, the overall time complexity is O(n)
.
Space Complexity: O(n)
The algorithm uses additional space for:
- The difference array
d
of sizen + 1
:O(n + 1) = O(n)
- A few integer variables (
ans
,s
):O(1)
The dominant space usage comes from the difference array d
, which stores the flip boundaries to track where flips start and end. Therefore, the total space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Flip Condition Logic
Pitfall: The condition current_flip_count % 2 == num
is counterintuitive. Many developers mistakenly think they should flip when the effective value is 0, leading to incorrect conditions like (current_flip_count + num) % 2 == 0
.
Why it happens: The logic seems backwards - we're checking if the parity of flips equals the original value, not the effective value.
Correct Understanding:
- When
current_flip_count % 2 == 0
(even flips): effective value = original value - When
current_flip_count % 2 == 1
(odd flips): effective value = 1 - original value - We need to flip when effective value is 0:
- If
num = 0
and even flips → effective is 0 → need flip - If
num = 1
and odd flips → effective is 0 → need flip - Both cases satisfy:
current_flip_count % 2 == num
- If
Solution:
# Add a comment or use a helper variable for clarity effective_value_is_zero = (current_flip_count % 2 == num) if effective_value_is_zero: # Perform flip
2. Off-by-One Error in Difference Array
Pitfall: Using flip_diff[i + k] += 1
instead of flip_diff[i + k] -= 1
, or incorrectly sizing the difference array as [0] * n
instead of [0] * (n + 1)
.
Why it happens: Confusion about how difference arrays work or forgetting that marking the end of a range requires decrementing.
Solution:
# Always remember: difference array needs n+1 size flip_diff = [0] * (n + 1) # NOT [0] * n # Marking a range [i, i+k-1]: flip_diff[i] += 1 # Start of range (increment) flip_diff[i + k] -= 1 # Position AFTER range ends (decrement)
3. Forgetting to Update current_flip_count
After Starting a New Flip
Pitfall: After marking the flip in the difference array, forgetting to immediately update current_flip_count += 1
for the current position.
Why it happens: The difference array update flip_diff[i] += 1
doesn't automatically affect current_flip_count
until the next iteration, but we need the effect immediately at position i
.
Incorrect Code:
if current_flip_count % 2 == num: if i + k > n: return -1 flip_diff[i] += 1 flip_diff[i + k] -= 1 # MISSING: current_flip_count += 1 total_flips += 1
Solution: Always update current_flip_count
immediately after starting a flip:
flip_diff[i] += 1 flip_diff[i + k] -= 1 current_flip_count += 1 # Don't forget this! total_flips += 1
4. Using Actual Flipping Instead of Virtual Tracking
Pitfall: Actually modifying the array elements during flips, which leads to O(n*k) time complexity.
Inefficient Approach:
# DON'T DO THIS - O(n*k) complexity
for i in range(n):
if nums[i] == 0:
if i + k > n:
return -1
for j in range(i, i + k):
nums[j] = 1 - nums[j] # Actually flipping
total_flips += 1
Solution: Use the difference array pattern to track flips virtually without modifying the original array, maintaining O(n) complexity.
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Queue Intro Think of the last time you stood in line to buy movie tickets The first person in line gets their ticket first and any newcomers join the end of the line This system where the first person to arrive is the first to be served is a queue in real
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Want a Structured Path to Master System Design Too? Don’t Miss This!