Facebook Pixel

672. Bulb Switcher II

Problem Description

You have a room with n light bulbs numbered from 1 to n. Initially, all bulbs are turned on. There are four buttons on the wall, each with a specific function:

  • Button 1: Flips the status of all bulbs (on becomes off, off becomes on)
  • Button 2: Flips the status of all even-numbered bulbs (2, 4, 6, ...)
  • Button 3: Flips the status of all odd-numbered bulbs (1, 3, 5, ...)
  • Button 4: Flips the status of bulbs at positions 3k + 1 where k = 0, 1, 2, ... (positions 1, 4, 7, 10, ...)

You must press buttons exactly presses times total. For each press, you can choose any of the four buttons. The goal is to find how many different final configurations of bulbs (patterns of on/off states) are possible after making exactly presses button presses.

For example, if you have 3 bulbs and 1 press:

  • Pressing Button 1 would flip all bulbs: [on, on, on] β†’ [off, off, off]
  • Pressing Button 2 would flip bulb 2: [on, on, on] β†’ [on, off, on]
  • Pressing Button 3 would flip bulbs 1 and 3: [on, on, on] β†’ [off, on, off]
  • Pressing Button 4 would flip bulb 1: [on, on, on] β†’ [off, on, on]

This gives 4 different possible states after 1 press.

The function should return the count of distinct possible bulb configurations after exactly presses button presses.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • No: This problem doesn't involve traditional graph structures with nodes and edges. We're dealing with bulbs and button press combinations.

Since we answered "no" to the graph question, we need to follow a different path in the flowchart. However, let's reconsider the problem structure:

Actually, if we think about it differently: Is it a graph?

  • Yes: We can model this as a state-space graph where each node represents a configuration of bulbs (on/off states), and edges represent transitions via button presses.

Is it a tree?

  • No: The state-space graph is not a tree because we can reach the same state through different paths (different sequences of button presses).

Is the problem related to directed acyclic graphs?

  • No: The state transitions can form cycles (pressing the same button twice returns to the original state).

Is the problem related to shortest paths?

  • No: We're not looking for the shortest path; we want to find all reachable states with exactly presses button presses.

Does the problem involve connectivity?

  • No: We're not checking if states are connected; we're counting distinct reachable states.

Does the problem have small constraints?

  • Yes: The number of buttons is fixed at 4, and we can observe that the pattern repeats after 6 bulbs (as shown in the solution where n = min(n, 6)). The state space is limited.

DFS/backtracking?

  • Yes: We need to explore all possible combinations of button presses to find all reachable states. This is effectively a DFS through the space of button press combinations.

Conclusion: The flowchart suggests using DFS/backtracking to explore all possible combinations of button presses. The solution uses bit manipulation to efficiently represent states and explores all valid combinations where the number of button presses has the same parity as the required number of presses (since pressing a button twice cancels out).

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

Intuition

The key insight is recognizing that pressing the same button twice cancels out its effect - each button toggles bulbs, so pressing it an even number of times returns bulbs to their original state. This means we only care about whether each button is pressed an odd or even number of times, not the exact sequence.

Since we have 4 buttons, there are only 2^4 = 16 possible combinations of buttons being pressed an odd/even number of times. We can represent each combination as a 4-bit number where bit i indicates if button i+1 is pressed an odd number of times.

Another crucial observation: if we need exactly presses button presses, and we choose to press k distinct buttons an odd number of times, then k must have the same parity as presses. For example, if presses = 3, we could press 1 button three times (odd), or press 3 different buttons once each (odd), but we couldn't achieve this by pressing 2 buttons since 2 has different parity than 3.

The pattern of bulb states also repeats after position 6. Why? Because:

  • Button 1 affects all positions
  • Button 2 has period 2 (even positions)
  • Button 3 has period 2 (odd positions)
  • Button 4 affects positions 1, 4, 7, 10, ... (pattern 3k+1)

The least common multiple of these patterns creates a cycle that repeats every 6 positions. So we only need to track the first 6 bulbs to determine all unique states.

The solution uses bit manipulation where each bulb's state is a bit (1 for on, 0 for off). We precompute the effect of each button as a bitmask (0b111111 for all bulbs, 0b010101 for even positions, etc.), then XOR these masks together based on which buttons are pressed an odd number of times. This efficiently generates all possible final states.

Learn more about Depth-First Search, Breadth-First Search and Math patterns.

Solution Approach

The implementation uses bit manipulation to efficiently represent and compute bulb states:

1. Button Operations as Bitmasks:

ops = (0b111111, 0b010101, 0b101010, 0b100100)

Each button's effect is encoded as a 6-bit number where bit position represents bulb position (rightmost bit is bulb 1):

  • Button 1: 0b111111 = all 6 bulbs
  • Button 2: 0b010101 = bulbs at even positions (2, 4, 6)
  • Button 3: 0b101010 = bulbs at odd positions (1, 3, 5)
  • Button 4: 0b100100 = bulbs at positions 1, 4 (following 3k+1 pattern)

2. Optimization for Large n:

n = min(n, 6)

Since the pattern repeats every 6 bulbs, we only need to track the first 6 positions. This reduces the state space dramatically.

3. Exploring All Valid Button Combinations:

for mask in range(1 << 4):
    cnt = mask.bit_count()
    if cnt <= presses and cnt % 2 == presses % 2:

We iterate through all 16 possible combinations (1 << 4) where each bit in mask indicates whether to press that button an odd number of times. The bit_count() gives us how many buttons are pressed odd times. We check:

  • cnt <= presses: Can't press more distinct buttons than total presses allowed
  • cnt % 2 == presses % 2: Parity must match (explained in intuition)

4. Computing Final State:

t = 0
for i, op in enumerate(ops):
    if (mask >> i) & 1:
        t ^= op

For each button that should be pressed an odd number of times (bit is 1 in mask), we XOR its operation pattern with our current state t. XOR perfectly models the toggle behavior.

5. Adjusting for Actual Bulb Count:

t &= (1 << 6) - 1  # Keep only first 6 bits
t >>= 6 - n        # Shift right to keep only n bits
vis.add(t)

We mask to keep only valid bits, then shift to normalize states for bulbs fewer than 6. This ensures different states are correctly identified.

6. Counting Unique States:

return len(vis)

The set vis automatically handles duplicates, giving us the count of unique possible states.

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 walk through a concrete example with n = 3 bulbs and presses = 2.

Initial Setup:

  • All 3 bulbs start as ON: [1, 1, 1]
  • We need exactly 2 button presses
  • Since we have only 3 bulbs, we work with 3-bit representations

Step 1: Define button operations for 3 bulbs

  • Button 1: Flips all β†’ 0b111 (bulbs 1, 2, 3)
  • Button 2: Flips even β†’ 0b010 (bulb 2)
  • Button 3: Flips odd β†’ 0b101 (bulbs 1, 3)
  • Button 4: Flips 3k+1 β†’ 0b001 (bulb 1)

Step 2: Find valid button combinations With 2 presses, we can either:

  • Press the same button twice (0 buttons pressed odd times)
  • Press two different buttons once each (2 buttons pressed odd times)

Both 0 and 2 have the same parity as 2 (all even), so both are valid.

Step 3: Generate all possible states

Let's examine each valid combination (represented as 4-bit mask):

  1. Mask = 0b0000 (no buttons pressed odd times):

    • Result: 0b000 (all OFF) β†’ State: [0, 0, 0]
  2. Mask = 0b0011 (buttons 1 and 2 pressed once):

    • XOR: 0b111 ^ 0b010 = 0b101 β†’ State: [1, 0, 1]
  3. Mask = 0b0101 (buttons 1 and 3 pressed once):

    • XOR: 0b111 ^ 0b101 = 0b010 β†’ State: [0, 1, 0]
  4. Mask = 0b0110 (buttons 2 and 3 pressed once):

    • XOR: 0b010 ^ 0b101 = 0b111 β†’ State: [1, 1, 1]
  5. Mask = 0b1001 (buttons 1 and 4 pressed once):

    • XOR: 0b111 ^ 0b001 = 0b110 β†’ State: [0, 1, 1]
  6. Mask = 0b1010 (buttons 2 and 4 pressed once):

    • XOR: 0b010 ^ 0b001 = 0b011 β†’ State: [1, 1, 0]
  7. Mask = 0b1100 (buttons 3 and 4 pressed once):

    • XOR: 0b101 ^ 0b001 = 0b100 β†’ State: [0, 0, 1]

Step 4: Count unique states We found 7 unique states:

  • [0, 0, 0]
  • [1, 0, 1]
  • [0, 1, 0]
  • [1, 1, 1]
  • [0, 1, 1]
  • [1, 1, 0]
  • [0, 0, 1]

Answer: 7 different configurations are possible

This demonstrates how the algorithm efficiently explores all valid button combinations using bit manipulation, where XOR operations naturally model the toggle behavior of the buttons.

Solution Implementation

1class Solution:
2    def flipLights(self, n: int, presses: int) -> int:
3        # Define the four button operations as bit patterns (for up to 6 lights)
4        # Each bit represents whether that light gets flipped (1) or not (0)
5        button_operations = (
6            0b111111,  # Button 1: Flip all lights (lights 1-6)
7            0b010101,  # Button 2: Flip even-positioned lights (2, 4, 6)
8            0b101010,  # Button 3: Flip odd-positioned lights (1, 3, 5)
9            0b100100   # Button 4: Flip lights at positions 3k+1 (1, 4)
10        )
11      
12        # Limit n to 6 since the pattern repeats after 6 lights
13        # This optimization works because the button patterns have a cycle of 6
14        n = min(n, 6)
15      
16        # Set to store unique final light states
17        unique_states = set()
18      
19        # Try all possible combinations of button presses (2^4 = 16 combinations)
20        for button_combination in range(1 << 4):
21            # Count how many buttons are pressed in this combination
22            buttons_pressed = button_combination.bit_count()
23          
24            # Check if this combination is valid:
25            # 1. We can't press more buttons than allowed
26            # 2. The parity must match (pressing a button twice cancels out)
27            if buttons_pressed <= presses and buttons_pressed % 2 == presses % 2:
28                # Initialize the final state (all lights initially on)
29                final_state = 0
30              
31                # Apply each selected button operation
32                for button_index, operation in enumerate(button_operations):
33                    # Check if this button is pressed in the current combination
34                    if (button_combination >> button_index) & 1:
35                        # XOR applies the flip operation
36                        final_state ^= operation
37              
38                # Mask to keep only the first 6 bits
39                final_state &= (1 << 6) - 1
40              
41                # Shift right to keep only the first n bits
42                # This handles cases where n < 6
43                final_state >>= (6 - n)
44              
45                # Add this unique state to our set
46                unique_states.add(final_state)
47      
48        # Return the number of unique states achievable
49        return len(unique_states)
50
1class Solution {
2    public int flipLights(int n, int presses) {
3        // Define the 4 button operations as bitmasks (for up to 6 lights)
4        // Button 1: Flip all lights (bits 1-6)
5        // Button 2: Flip lights at even positions (2, 4, 6)
6        // Button 3: Flip lights at odd positions (1, 3, 5)  
7        // Button 4: Flip lights at positions 3k+1 (1, 4)
8        int[] buttonOperations = new int[] {
9            0b111111,  // Button 1: flip all 6 lights
10            0b010101,  // Button 2: flip even positions (2,4,6)
11            0b101010,  // Button 3: flip odd positions (1,3,5)
12            0b100100   // Button 4: flip positions 1,4 (3k+1)
13        };
14      
15        // Set to store unique light states after operations
16        Set<Integer> uniqueStates = new HashSet<>();
17      
18        // Optimize: only need to consider first 6 lights due to pattern repetition
19        n = Math.min(n, 6);
20      
21        // Try all possible combinations of button presses (2^4 = 16 combinations)
22        for (int buttonMask = 0; buttonMask < (1 << 4); ++buttonMask) {
23            // Count how many buttons are pressed in this combination
24            int buttonPressCount = Integer.bitCount(buttonMask);
25          
26            // Check if this combination is valid:
27            // 1. Total presses doesn't exceed allowed presses
28            // 2. Parity matches (pressing a button twice cancels out)
29            if (buttonPressCount <= presses && buttonPressCount % 2 == presses % 2) {
30                // Apply the button operations to get final light state
31                int lightState = 0;
32                for (int buttonIndex = 0; buttonIndex < 4; ++buttonIndex) {
33                    // If this button is pressed in current combination
34                    if (((buttonMask >> buttonIndex) & 1) == 1) {
35                        // XOR to apply the button's effect on lights
36                        lightState ^= buttonOperations[buttonIndex];
37                    }
38                }
39              
40                // Mask to keep only 6 bits (6 lights)
41                lightState &= ((1 << 6) - 1);
42              
43                // Right shift to keep only n lights (discard extra bits)
44                lightState >>= (6 - n);
45              
46                // Add this unique state to the set
47                uniqueStates.add(lightState);
48            }
49        }
50      
51        // Return the count of unique light states possible
52        return uniqueStates.size();
53    }
54}
55
1class Solution {
2public:
3    int flipLights(int n, int presses) {
4        // Optimize by considering only first 6 lights (pattern repeats)
5        n = min(n, 6);
6      
7        // Define the 4 button operations as bitmasks (for first 6 lights)
8        // Button 1: Flip all lights (bits 0-5)
9        // Button 2: Flip even position lights (0-indexed: 1, 3, 5)
10        // Button 3: Flip odd position lights (0-indexed: 0, 2, 4)
11        // Button 4: Flip lights at positions 3k+1 (1-indexed: 1, 4 => 0-indexed: 0, 3)
12        vector<int> buttonOperations = {
13            0b111111,  // Button 1: flip all 6 lights
14            0b010101,  // Button 2: flip lights at positions 2, 4, 6 (1-indexed)
15            0b101010,  // Button 3: flip lights at positions 1, 3, 5 (1-indexed)
16            0b100100   // Button 4: flip lights at positions 1, 4 (1-indexed)
17        };
18      
19        // Set to store unique light states after operations
20        unordered_set<int> uniqueStates;
21      
22        // Try all possible combinations of button presses (2^4 = 16 combinations)
23        for (int buttonMask = 0; buttonMask < (1 << 4); ++buttonMask) {
24            // Count how many buttons are pressed in this combination
25            int buttonPressCount = __builtin_popcount(buttonMask);
26          
27            // Skip if button count doesn't match constraints:
28            // 1. Can't press more buttons than allowed
29            // 2. Parity must match (pressing same button twice cancels out)
30            if (buttonPressCount > presses || buttonPressCount % 2 != presses % 2) {
31                continue;
32            }
33          
34            // Calculate final light state after applying selected operations
35            int lightState = 0;
36            for (int buttonIndex = 0; buttonIndex < 4; ++buttonIndex) {
37                // Check if this button is pressed in current combination
38                if ((buttonMask >> buttonIndex) & 1) {
39                    // XOR to apply the button operation
40                    lightState ^= buttonOperations[buttonIndex];
41                }
42            }
43          
44            // Keep only the first 6 bits (representing 6 lights)
45            lightState &= (1 << 6) - 1;
46          
47            // Shift right to keep only n lights (remove extra bits if n < 6)
48            lightState >>= (6 - n);
49          
50            // Add this unique state to the set
51            uniqueStates.insert(lightState);
52        }
53      
54        // Return the count of unique light configurations
55        return uniqueStates.size();
56    }
57};
58
1function flipLights(n: number, presses: number): number {
2    // Optimize by considering only first 6 lights (pattern repeats)
3    n = Math.min(n, 6);
4  
5    // Define the 4 button operations as bitmasks (for first 6 lights)
6    // Button 1: Flip all lights (bits 0-5)
7    // Button 2: Flip even position lights (1-indexed: 2, 4, 6 => 0-indexed: 1, 3, 5)
8    // Button 3: Flip odd position lights (1-indexed: 1, 3, 5 => 0-indexed: 0, 2, 4)
9    // Button 4: Flip lights at positions 3k+1 (1-indexed: 1, 4 => 0-indexed: 0, 3)
10    const buttonOperations: number[] = [
11        0b111111,  // Button 1: flip all 6 lights
12        0b010101,  // Button 2: flip lights at positions 2, 4, 6 (1-indexed)
13        0b101010,  // Button 3: flip lights at positions 1, 3, 5 (1-indexed)
14        0b100100   // Button 4: flip lights at positions 1, 4 (1-indexed)
15    ];
16  
17    // Set to store unique light states after operations
18    const uniqueStates: Set<number> = new Set<number>();
19  
20    // Try all possible combinations of button presses (2^4 = 16 combinations)
21    for (let buttonMask = 0; buttonMask < (1 << 4); buttonMask++) {
22        // Count how many buttons are pressed in this combination
23        let buttonPressCount = 0;
24        let tempMask = buttonMask;
25        while (tempMask > 0) {
26            buttonPressCount += tempMask & 1;
27            tempMask >>= 1;
28        }
29      
30        // Skip if button count doesn't match constraints:
31        // 1. Can't press more buttons than allowed
32        // 2. Parity must match (pressing same button twice cancels out)
33        if (buttonPressCount > presses || buttonPressCount % 2 !== presses % 2) {
34            continue;
35        }
36      
37        // Calculate final light state after applying selected operations
38        let lightState = 0;
39        for (let buttonIndex = 0; buttonIndex < 4; buttonIndex++) {
40            // Check if this button is pressed in current combination
41            if ((buttonMask >> buttonIndex) & 1) {
42                // XOR to apply the button operation
43                lightState ^= buttonOperations[buttonIndex];
44            }
45        }
46      
47        // Keep only the first 6 bits (representing 6 lights)
48        lightState &= (1 << 6) - 1;
49      
50        // Shift right to keep only n lights (remove extra bits if n < 6)
51        lightState >>= (6 - n);
52      
53        // Add this unique state to the set
54        uniqueStates.add(lightState);
55    }
56  
57    // Return the count of unique light configurations
58    return uniqueStates.size;
59}
60

Time and Space Complexity

Time Complexity: O(1)

The algorithm iterates through all possible combinations of button presses using a bitmask from 0 to 2^4 - 1 (16 iterations). For each mask value:

  • bit_count() operation takes O(1) time
  • The inner loop iterates through 4 operations (fixed constant)
  • XOR and bit manipulation operations are O(1)

Since we have a fixed number of iterations (16 outer loop iterations Γ— 4 inner loop iterations), the overall time complexity is O(16 Γ— 4) = O(1).

Space Complexity: O(1)

The space usage includes:

  • ops tuple with 4 fixed binary values: O(1)
  • vis set that stores unique light configurations. In the worst case, all 16 possible masks could generate unique configurations, but since we're dealing with at most 6 lights, the maximum number of unique states is bounded by 2^6 = 64. Therefore, the set size is O(1)
  • A few integer variables (mask, cnt, t, etc.): O(1)

The total space complexity is O(1) as all data structures have a constant upper bound independent of the input size.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Misunderstanding Button Press Parity

The most common mistake is not recognizing that pressing the same button twice cancels out its effect (due to XOR operation). This leads to incorrectly counting states.

Pitfall Example:

# WRONG: Trying all combinations up to 'presses' count
for num_presses in range(presses + 1):
    # This doesn't account for button cancellation
    states.add(some_combination)

Solution: Only consider combinations where buttons are pressed an odd number of times, and ensure the total parity matches:

if buttons_pressed <= presses and buttons_pressed % 2 == presses % 2:
    # Valid combination

2. Incorrect Bit Manipulation for Different Values of n

When n < 6, simply using the 6-bit patterns without adjustment leads to incorrect state counting.

Pitfall Example:

# WRONG: Not adjusting for actual bulb count
final_state ^= operation
unique_states.add(final_state)  # This includes bits beyond position n

Solution: Properly mask and shift the bits to normalize states:

final_state &= (1 << 6) - 1  # Keep only first 6 bits
final_state >>= (6 - n)      # Shift to keep only n bits

3. Not Recognizing the Pattern Repetition

Attempting to handle all n bulbs individually leads to exponential complexity and wrong results for large n.

Pitfall Example:

# WRONG: Creating masks for all n bulbs
button1_mask = (1 << n) - 1  # All n bulbs
button4_mask = 0
for k in range(n):
    if k % 3 == 0:
        button4_mask |= (1 << k)

Solution: Recognize that the pattern repeats every 6 bulbs (LCM of button periodicities):

n = min(n, 6)  # Pattern repeats, so we only need first 6

4. Incorrect Button 4 Pattern

Misunderstanding the "3k + 1" pattern for Button 4, often implementing it as positions 1, 3, 6, 9... instead of 1, 4, 7, 10...

Pitfall Example:

# WRONG: Incorrect interpretation of 3k+1
button4 = 0b001001  # Positions 1 and 4 (wrong pattern)

Solution: Correctly implement 3k+1 where k = 0, 1, 2...:

button4 = 0b100100  # Positions 1 (k=0) and 4 (k=1)

5. Off-by-One Errors in Bit Positions

Confusing 0-indexed vs 1-indexed bulb positions when creating bit masks.

Pitfall Example:

# WRONG: Mixing up indexing
button2 = 0b101010  # This would be positions 2, 4, 6 in 0-indexed

Solution: Be consistent with the rightmost bit representing bulb 1:

button2 = 0b010101  # Bulbs 2, 4, 6 (even positions, 1-indexed)
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".

What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?


Recommended Readings

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

Load More