Facebook Pixel

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 from nums
  • 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] and k = 1, you can flip each 0 individually (2 flips total)
  • If nums = [1,1,0] and k = 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] and k = 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).

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 and s is even (even flips), the position remains 0
  • If nums[i] = 1 and s 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 size n+1 to mark flip boundaries
  • s: Running sum to track the number of active flips at current position
  • ans: Counter for total number of flips performed

Algorithm Steps:

  1. Initialize variables:

    • Create difference array d with all zeros
    • Set ans = 0 (flip counter) and s = 0 (active flips counter)
  2. 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 sum s to account for flips starting/ending at position i
  3. Check if current position needs flipping:

    if s % 2 == x:
    • If s % 2 == x, the effective value at position i is 0
    • This happens when:
      • x = 0 and s is even (no net flip effect)
      • x = 1 and s is odd (flipped to 0)
  4. 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
  5. Return result:

    • After processing all positions, return ans as the minimum flips needed

Why the difference array works:

  • When we set d[i] += 1 and d[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 Evaluator

Example 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] += 1d = [1,0,0,0] (mark flip start)
    • d[2] -= 1d = [1,0,-1,0] (mark flip end)
    • s += 1s = 1 (one flip now active)
    • ans += 1ans = 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] += 1d = [1,1,-1,0] (mark flip start)
    • d[3] -= 1d = [1,1,-1,-1] (mark flip end)
    • s += 1s = 2 (two flips now active)
    • ans += 1ans = 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:

  1. First flip at index 0: [0,1,0] → [1,0,0]
  2. 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 size n + 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

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.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of these properties could exist for a graph but not a tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More