A clear explanation of the Push Dominoes problem using force propagation and a two-pass scan.
Problem Restatement
We are given a string dominoes.
Each character describes the initial state of one domino:
| Character | Meaning |
|---|---|
"L" | Domino is pushed to the left |
"R" | Domino is pushed to the right |
"." | Domino is standing upright |
After each second, a falling domino pushes the adjacent upright domino in the same direction.
If a standing domino receives equal force from both sides at the same time, it stays upright.
A domino already falling or already fallen does not pass extra force through another falling domino.
Return the final state of all dominoes. The constraints allow strings up to length 10^5.
Input and Output
| Item | Meaning |
|---|---|
| Input | A string dominoes |
| Output | Final domino state as a string |
| Characters | "L", "R", and "." |
| Main rule | Forces spread one cell per second |
| Tie rule | Equal opposite forces cancel |
Function shape:
class Solution:
def pushDominoes(self, dominoes: str) -> str:
...Examples
Example 1:
dominoes = "RR.L"The final state is:
"RR.L"The left "R" side does not push through an already falling domino, so the middle state stays as given.
Example 2:
dominoes = ".L.R...LR..L.."The final state is:
"LL.RR.LLRRLL.."First Thought: Simulate Every Second
A direct approach is to simulate time.
At each second:
- Look at every upright domino.
- Check whether it is pushed from the left, from the right, or both.
- Build the next state.
- Repeat until nothing changes.
This can work for small strings, but it is too slow.
A force may travel across a long row of dots. With n dominoes, simulation can take many rounds, and each round scans the string.
That can become:
O(n^2)We need a linear method.
Key Insight
A domino’s final state depends on the nearest effective force from the left and the nearest effective force from the right.
A right-pushing force "R" travels to the right until blocked by an "L".
A left-pushing force "L" travels to the left until blocked by an "R".
We can compute net force with two scans:
- Left to right: measure force from
"R". - Right to left: measure force from
"L".
If the right force is stronger, the domino becomes "R".
If the left force is stronger, the domino becomes "L".
If they are equal, the domino stays ".".
Algorithm
Let n = len(dominoes).
Create an array forces of length n, initially all 0.
First pass from left to right:
- If we see
"R", set force ton. - If we see
"L", reset force to0. - If we see
".", decrease force by1, but not below0. - Add this force to
forces[i].
Second pass from right to left:
- If we see
"L", set force ton. - If we see
"R", reset force to0. - If we see
".", decrease force by1, but not below0. - Subtract this force from
forces[i].
Finally:
| Net force | Final character |
|---|---|
| Positive | "R" |
| Negative | "L" |
| Zero | "." |
Walkthrough
Use:
dominoes = "R...L"The "R" pushes right.
The "L" pushes left.
Positions:
0 1 2 3 4
R . . . LThe middle dot at index 2 receives equal force from both sides.
Final state:
"RR.LL"Index 1 is closer to "R", so it becomes "R".
Index 3 is closer to "L", so it becomes "L".
Index 2 is equally close to both, so it stays ".".
Correctness
The left-to-right pass computes, for each position, the strength of the nearest unblocked "R" force coming from the left.
When the scan sees "R", a new rightward force begins. When it sees "L", that rightward force is blocked. Between them, the force decreases by one per position, matching the one-cell-per-second propagation rule.
The right-to-left pass computes the analogous nearest unblocked "L" force from the right.
For each domino, the final direction is determined by comparing these two arrival strengths. A larger rightward force means the "R" wave arrives earlier, so the domino falls right. A larger leftward force means the "L" wave arrives earlier, so the domino falls left. Equal force means both waves arrive at the same time, so the domino stays upright.
Thus the algorithm assigns every domino exactly the final state implied by the simultaneous process.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Two linear scans and one output scan |
| Space | O(n) | The force array and output array |
Implementation
class Solution:
def pushDominoes(self, dominoes: str) -> str:
n = len(dominoes)
forces = [0] * n
force = 0
for i, ch in enumerate(dominoes):
if ch == "R":
force = n
elif ch == "L":
force = 0
else:
force = max(force - 1, 0)
forces[i] += force
force = 0
for i in range(n - 1, -1, -1):
ch = dominoes[i]
if ch == "L":
force = n
elif ch == "R":
force = 0
else:
force = max(force - 1, 0)
forces[i] -= force
result = []
for force in forces:
if force > 0:
result.append("R")
elif force < 0:
result.append("L")
else:
result.append(".")
return "".join(result)Code Explanation
The force array stores the net force at each position:
forces = [0] * nIn the left-to-right pass:
if ch == "R":
force = n
elif ch == "L":
force = 0
else:
force = max(force - 1, 0)An "R" starts a strong rightward force.
An "L" blocks rightward force.
A dot receives the previous force weakened by one.
We add that rightward force:
forces[i] += forceIn the right-to-left pass, we do the same for leftward force, but subtract it:
forces[i] -= forceFinally, positive net force becomes "R", negative net force becomes "L", and zero becomes ".".
Testing
def run_tests():
s = Solution()
assert s.pushDominoes("RR.L") == "RR.L"
assert s.pushDominoes(".L.R...LR..L..") == "LL.RR.LLRRLL.."
assert s.pushDominoes("R...L") == "RR.LL"
assert s.pushDominoes("R..L") == "RRLL"
assert s.pushDominoes("...") == "..."
assert s.pushDominoes("L....") == "L...."
assert s.pushDominoes("....R") == "....R"
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
"RR.L" | Confirms force does not pass through a fallen domino |
| Standard example | Confirms mixed interactions |
"R...L" | Confirms equal forces leave middle upright |
"R..L" | Confirms no middle when distance is even |
| All dots | Confirms no force |
| Leading left push | Confirms left force does not move right |
| Trailing right push | Confirms right force does not move left |