Bulb Switcher II
MedPrompt
There is a room with n bulbs labeled from 1 to n that all are turned on initially, and four buttons on the wall. Each of the four buttons has a different functionality where:
- Button 1: Flips the status of all the bulbs.
- Button 2: Flips the status of all the bulbs with even labels (i.e.,
2, 4, ...). - Button 3: Flips the status of all the bulbs with odd labels (i.e.,
1, 3, ...). - Button 4: Flips the status of all the bulbs with a label
j = 3k + 1wherek = 0, 1, 2, ...(i.e.,1, 4, 7, 10, ...).
You must make exactly presses button presses in total. For each press, you may pick any of the four buttons to press.
Given the two integers n and presses, return the number of different possible statuses after performing all presses button presses.
Example 1:
Input: n = 1, presses = 1
Output: 2
Explanation: Status can be:
- [off] by pressing button 1
- [on] by pressing button 2Example 2:
Input: n = 2, presses = 1
Output: 3
Explanation: Status can be:
- [off, off] by pressing button 1
- [on, off] by pressing button 2
- [off, on] by pressing button 3Example 3:
Input: n = 3, presses = 1
Output: 4
Explanation: Status can be:
- [off, off, off] by pressing button 1
- [off, on, off] by pressing button 2
- [on, off, on] by pressing button 3
- [off, on, on] by pressing button 4
Constraints:
1 <= n <= 10000 <= presses <= 1000
Approaches
3 approaches with complexity analysis and trade-offs.
This approach simulates the button presses level by level using Breadth-First Search (BFS). It starts with the initial state (all bulbs on) and, for each press, explores all possible next states by applying each of the four button operations. A set is used to keep track of unique states at each level to avoid redundant computations.
Algorithm
- Model the problem as a state transition graph where nodes are bulb configurations and edges represent button presses.
- The goal is to find the number of unique states reachable in exactly
pressessteps. - Use a Breadth-First Search (BFS) approach. Start with a set containing just the initial state (all bulbs 'on').
- Iterate
pressestimes. In each iteration, compute a new set of states by taking every state from the previous set and applying each of the four button operations. - The state of
nbulbs can be represented by a string of '0's and '1's. This allows for easy storage in aHashSetto track unique states. - The algorithm proceeds as follows:
- Initialize a
Set<String>calledcurrentStateswith the initial state (a string ofn'1's). - Loop
pfrom 1 topresses: a. Create a newSet<String>callednextStates. b. For eachstateincurrentStates: i. Apply button 1 operation tostateand add the result tonextStates. ii. Apply button 2, 3, and 4 operations similarly and add their results tonextStates. c. ReplacecurrentStateswithnextStates. - The size of the final
currentStatesset is the answer.
- Initialize a
Walkthrough
This method directly translates the problem into a simulation. We treat each possible configuration of the n bulbs as a state. Starting from the initial state where all bulbs are on, we simulate the effect of presses button presses. Since the order of presses within a single step doesn't matter, we can think of this as a level-by-level exploration, characteristic of BFS. At each level (representing one press), we generate all possible new states from the states of the previous level. A HashSet is crucial to store the states at each level, as it automatically handles duplicates, ensuring we only count unique configurations.
import java.util.Arrays;import java.util.HashSet;import java.util.Set; class Solution { public int flipLights(int n, int presses) { if (presses == 0) { return 1; } Set<String> currentStates = new HashSet<>(); char[] initialChars = new char[n]; Arrays.fill(initialChars, '1'); currentStates.add(new String(initialChars)); for (int i = 0; i < presses; i++) { Set<String> nextStates = new HashSet<>(); for (String s : currentStates) { nextStates.add(applyOp(s.toCharArray(), 1)); nextStates.add(applyOp(s.toCharArray(), 2)); nextStates.add(applyOp(s.toCharArray(), 3)); nextStates.add(applyOp(s.toCharArray(), 4)); } currentStates = nextStates; } return currentStates.size(); } private String applyOp(char[] bulbs, int opType) { int n = bulbs.length; switch (opType) { case 1: // Flip all for (int i = 0; i < n; i++) { bulbs[i] = (bulbs[i] == '1' ? '0' : '1'); } break; case 2: // Flip even (labels 2, 4, ... -> indices 1, 3, ...) for (int i = 1; i < n; i += 2) { bulbs[i] = (bulbs[i] == '1' ? '0' : '1'); } break; case 3: // Flip odd (labels 1, 3, ... -> indices 0, 2, ...) for (int i = 0; i < n; i += 2) { bulbs[i] = (bulbs[i] == '1' ? '0' : '1'); } break; case 4: // Flip 3k+1 (labels 1, 4, ... -> indices 0, 3, ...) for (int i = 0; i < n; i += 3) { bulbs[i] = (bulbs[i] == '1' ? '0' : '1'); } break; } return new String(bulbs); }}Complexity
Time
O(presses * S * n), where S is the maximum number of unique states at any level (at most 8). This simplifies to O(presses * n). Given the constraints, this could be around 1000 * 1000 = 10^6 operations, which, combined with string manipulations, is slow.
Space
O(S * n), where S is the maximum number of unique states at any level (at most 8). Since S is a small constant, this simplifies to O(n) to store the set of states as strings.
Trade-offs
Pros
Simple to understand and implement as it directly models the problem statement.
Guaranteed to be correct if it runs within the time limits.
Cons
Highly inefficient for the given constraints on
nandpresses.The state representation (a string of length up to 1000) is large, making string operations and storage in the hash set computationally expensive.
Likely to result in a 'Time Limit Exceeded' error on most platforms.
This approach significantly improves upon the brute-force simulation by leveraging key properties of the button operations. It recognizes that the state of any bulb i is determined by its properties modulo 2 and 3. This creates a repeating pattern of bulb behaviors, allowing us to only simulate the states for a small, constant number of bulbs (e.g., n=3 is sufficient) to distinguish all possible outcomes. This state compression makes the simulation extremely fast.
Algorithm
- Observe that the four button operations affect bulbs based on their labels: all, even, odd, and
3k+1. - The effect of an operation on bulb
idepends oni mod 2andi mod 3. The combined properties repeat with a period oflcm(2, 3) = 6. - This periodicity implies that the state of the entire sequence of bulbs is determined by the state of the first few bulbs. Analysis shows that the first 3 bulbs are sufficient to uniquely distinguish all 8 possible transformations.
- Therefore, we can cap
natmin(n, 3). This drastically reduces the state space. - With a small
n, we can represent the state of the bulbs as a single integer bitmask, making operations very fast. - The algorithm is:
- Handle the base case
presses == 0, returning 1. - Reduce
ntomin(n, 3). - Determine the set of achievable transformations based on
presses.presses = 1: 4 specific transformations are possible.presses = 2: 7 specific transformations are possible.presses >= 3: All 8 possible transformations are achievable.
- Pre-compute the bitmasks for the 4 basic operations for the reduced
n. - Generate the final states by applying the achievable transformations to the initial state mask.
- Store the results in a
HashSetand return its size.
- Handle the base case
Walkthrough
The key observation is that the bulb states exhibit a periodic pattern. A bulb's state is affected by operations that depend on its index being even/odd (period 2) or of the form 3k+1 (related to period 3). The combined behavior repeats every lcm(2,3)=6 bulbs. A deeper look reveals that the first 3 bulbs are enough to distinguish all 8 unique transformations that can arise from combinations of button presses. For example, (op1, op2, op3, op4) result in four distinct states for n=3: (0,0,0), (1,0,1), (0,1,0), and (0,1,1). By capping n at min(n, 3), we can use bitmasks for an efficient simulation.
import java.util.HashSet;import java.util.Set; class Solution { public int flipLights(int n, int presses) { if (presses == 0) { return 1; } // The state of the first 3 bulbs is sufficient to distinguish all 8 transformations. n = Math.min(n, 3); Set<Integer> finalStates = new HashSet<>(); int initialMask = (1 << n) - 1; // All bulbs on // Generate masks for the 4 operations for the current n int[] opMasks = new int[4]; opMasks[0] = (1 << n) - 1; // Button 1: all for (int i = 1; i < n; i += 2) opMasks[1] |= (1 << i); // Button 2: even for (int i = 0; i < n; i += 2) opMasks[2] |= (1 << i); // Button 3: odd for (int i = 0; i < n; i += 3) opMasks[3] |= (1 << i); // Button 4: 3k+1 if (presses == 1) { for (int mask : opMasks) { finalStates.add(initialMask ^ mask); } } else if (presses == 2) { // Achievable with k=0 or k=2 presses finalStates.add(initialMask); // k=0 for (int i = 0; i < 4; i++) { for (int j = i + 1; j < 4; j++) { finalStates.add(initialMask ^ opMasks[i] ^ opMasks[j]); } } } else { // presses >= 3 // All 8 transformations are achievable for (int i = 0; i < 16; i++) { int finalMask = initialMask; if ((i & 1) > 0) finalMask ^= opMasks[0]; if ((i & 2) > 0) finalMask ^= opMasks[1]; if ((i & 4) > 0) finalMask ^= opMasks[2]; if ((i & 8) > 0) finalMask ^= opMasks[3]; finalStates.add(finalMask); } } return finalStates.size(); }}Note: The code above for presses=2 and presses>=3 is a simplified way to generate the reachable states, which correctly captures the number of unique outcomes for the reduced n.
Complexity
Time
O(1), because both `n` and `presses` are effectively capped at small constant values (3 and 3, respectively) for the logic, making the computations independent of the input size.
Space
O(1), as the `HashSet` and other variables used are of a small, constant maximum size.
Trade-offs
Pros
Very efficient, effectively constant time, as it reduces the problem size.
Handles large
nwithout any performance penalty.
Cons
Requires non-trivial insights into the problem's structure (periodicity and state distinguishability).
The implementation can be slightly more complex than the final mathematical approach, as it still involves some simulation logic.
This is the most optimal approach, bypassing simulation entirely. It involves a deep mathematical and combinatorial analysis to find a direct, closed-form solution. By analyzing the algebraic relationships between the button operations, the constraints on the number of presses, and how the number of bulbs n affects the distinguishability of the final states, we can determine the exact number of outcomes with simple conditional logic.
Algorithm
- The solution is derived from a complete mathematical analysis of the problem's state space.
- Insight 1: Operation Equivalence. Pressing a button twice cancels itself out. The order of presses does not matter. This means we only care about the parity (odd/even) of presses for each button.
- Insight 2: Operation Dependency. Button 1's effect is the same as pressing Button 2 and Button 3. This dependency (
op1 = op2 XOR op3) reduces the number of independent operations, leading to at most 8 unique final transformations. - Insight 3: Achievability. The set of transformations achievable with exactly
pressespresses depends on the parity ofpresses. A transformation corresponding to a combination ofkodd-parity button presses is possible only ifk <= pressesandk % 2 == presses % 2. - Insight 4: Distinguishability. The number of unique final states depends on
n. Forn=1andn=2, some of the 8 transformations produce identical results. Forn>=3, all 8 transformations result in unique states. - By combining these insights, we can determine the exact number of outcomes for each combination of
nandpressesand implement the solution using a simple set of conditional checks.
Walkthrough
The problem can be solved by analyzing the constraints and properties of the system. We can determine the number of unique states by considering the values of n and presses in a few distinct cases.
-
Case
presses = 0: No buttons are pressed. The bulbs remain in their initial 'on' state. There is only 1 possible status. -
Case
n = 1: There is only one bulb. Button 2 (even labels) has no effect. Buttons 1, 3, and 4 all flip the first bulb. With one or more presses, we can either leave the bulb on (e.g., press button 2) or turn it off (e.g., press button 1). Thus, there are 2 possible statuses ifpresses > 0. -
Case
n = 2: There are two bulbs.- If
presses = 1, we can achieve 3 states:[off, off](B1),[on, off](B2),[off, on](B3 or B4). Total 3 statuses. - If
presses >= 2, we can achieve all 4 possible states for two bulbs:[on, on],[on, off],[off, on],[off, off]. Total 4 statuses.
- If
-
Case
n >= 3: All 8 fundamental transformations produce unique states on the first 3 bulbs.- If
presses = 1, we can use any of the 4 buttons, resulting in 4 unique statuses. - If
presses = 2, analysis shows that 7 of the 8 transformations are achievable, resulting in 7 unique statuses. - If
presses >= 3, all 8 transformations are achievable, resulting in 8 unique statuses.
- If
This case analysis covers all possibilities and can be implemented with a simple set of if-else statements.
class Solution { public int flipLights(int n, int presses) { // No presses, all bulbs remain on. if (presses == 0) { return 1; } // Case for n = 1 bulb if (n == 1) { return 2; // Can be on or off. } // Case for n = 2 bulbs if (n == 2) { if (presses == 1) { return 3; } else { // presses >= 2 return 4; } } // Case for n >= 3 bulbs if (presses == 1) { return 4; } else if (presses == 2) { return 7; } else { // presses >= 3 return 8; } }}Complexity
Time
O(1), as the solution consists of a few conditional checks on the input values.
Space
O(1), as no extra space is used that depends on the input size.
Trade-offs
Pros
Extremely efficient, providing a solution in constant time and space.
The code is very simple, concise, and easy to read once the logic is understood.
Cons
The logic is non-obvious and requires a deep mathematical analysis of the problem's structure.
It can be very difficult to derive this solution from scratch under time pressure, such as in a coding interview.
Solutions
Solution
class Solution { public int flipLights ( int n , int presses ) { int [] ops = new int [] { 0b111111 , 0b010101 , 0b101010 , 0b100100 }; Set < Integer > vis = new HashSet <>(); n = Math . min ( n , 6 ); for ( int mask = 0 ; mask < 1 << 4 ; ++ mask ) { int cnt = Integer . bitCount ( mask ); if ( cnt <= presses && cnt % 2 == presses % 2 ) { int t = 0 ; for ( int i = 0 ; i < 4 ; ++ i ) { if ((( mask >> i ) & 1 ) == 1 ) { t ^= ops [ i ]; } } t &= (( 1 << 6 ) - 1 ); t >>= ( 6 - n ); vis . add ( t ); } } return vis . size (); } }class Solution { public: int flipLights ( int n , int presses ) { n = min ( n , 6 ); vector < int > ops = { 0b111111 , 0b010101 , 0b101010 , 0b100100 }; unordered_set < int > vis ; for ( int mask = 0 ; mask < 1 << 4 ; ++ mask ) { int cnt = __builtin_popcount ( mask ); if ( cnt > presses || cnt % 2 != presses % 2 ) continue ; int t = 0 ; for ( int i = 0 ; i < 4 ; ++ i ) { if ( mask >> i & 1 ) { t ^= ops [ i ]; } } t &= ( 1 << 6 ) - 1 ; t >>= ( 6 - n ); vis . insert ( t ); } return vis . size (); } };class Solution : def flipLights ( self , n : int , presses : int ) -> int : ops = ( 0b111111 , 0b010101 , 0b101010 , 0b100100 ) n = min ( n , 6 ) vis = set () for mask in range ( 1 << 4 ): cnt = mask . bit_count () if cnt <= presses and cnt % 2 == presses % 2 : t = 0 for i , op in enumerate ( ops ): if ( mask >> i ) & 1 : t ^= op t &= ( 1 << 6 ) - 1 t >>= 6 - n vis . add ( t ) return len ( vis )Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.