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
wherek = 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).
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, ...
(pattern3k+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 (following3k+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 allowedcnt % 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 EvaluatorExample 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):
-
Mask = 0b0000 (no buttons pressed odd times):
- Result:
0b000
(all OFF) β State:[0, 0, 0]
- Result:
-
Mask = 0b0011 (buttons 1 and 2 pressed once):
- XOR:
0b111 ^ 0b010 = 0b101
β State:[1, 0, 1]
- XOR:
-
Mask = 0b0101 (buttons 1 and 3 pressed once):
- XOR:
0b111 ^ 0b101 = 0b010
β State:[0, 1, 0]
- XOR:
-
Mask = 0b0110 (buttons 2 and 3 pressed once):
- XOR:
0b010 ^ 0b101 = 0b111
β State:[1, 1, 1]
- XOR:
-
Mask = 0b1001 (buttons 1 and 4 pressed once):
- XOR:
0b111 ^ 0b001 = 0b110
β State:[0, 1, 1]
- XOR:
-
Mask = 0b1010 (buttons 2 and 4 pressed once):
- XOR:
0b010 ^ 0b001 = 0b011
β State:[1, 1, 0]
- XOR:
-
Mask = 0b1100 (buttons 3 and 4 pressed once):
- XOR:
0b101 ^ 0b001 = 0b100
β State:[0, 0, 1]
- XOR:
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 takesO(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 by2^6 = 64
. Therefore, the set size isO(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)
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
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Want a Structured Path to Master System Design Too? Donβt Miss This!