Push Dominoes — Medium Problem & Solution
A row of dominoes is described by a string: 'L' was pushed left, 'R' was pushed right, and '.' is still standing.
- Difficulty: Medium
- Topics: Strings, Dynamic Programming, Two Pointers, Simulation
- Asked at: Amazon, Google, Flipkart
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
A row of dominoes is described by a string: 'L' was pushed left, 'R' was pushed right, and '.' is still standing.
Each second, a falling domino pushes the adjacent standing one in the same direction. A standing domino with a falling domino on both sides stays upright, because the forces cancel.
Return the final state once nothing moves any more.
Example 1
Input: dominoes = "RR.L"
Output: "RR.L"
Explanation: The third domino is pushed left by the fourth; the second one was already down.
Example 2
Input: dominoes = ".L.R...LR..L.."
Output: "LL.RR.LLRRLL.."
Explanation: In the run `R...L` the outer two meet in the middle and the exact centre stays standing.
Example 3
Input: dominoes = "..."
Output: "..."
Explanation: Nothing was ever pushed.
Constraints
1 <= dominoes.length <= 100000dominoes[i] is 'L', 'R' or '.'
How to solve Push Dominoes
The outcome of each maximal run of dots depends only on the two non-dot characters bounding it. Padding the row with a leftward push on the far left and a rightward push on the far right makes every run bounded, and then each of the four cases has a closed form.
Approach
- Form
s = "L" + dominoes + "R"; the sentinels push away from the row, so they never disturb it. - Walk
iover the non-dot positions, keepingprevas the previous one. - Emit
s[prev](unless it is the left sentinel) followed by thegap = i - prev - 1dots resolved by case: same characters fill with that character,L…Rstays all dots,R…Lfills halfR, halfL, with a single.in the middle when the gap is odd.
Why it works
Forces only travel along a run of standing dominoes, so a run's fate is decided by its two ends. With R…L the two waves advance at the same rate and meet after gap/2 steps: an odd gap leaves a domino pushed equally from both sides, which the rules say stays up. The sentinels encode the true boundary behaviour — a leading …L really does drag everything before it down, and a trailing R… really does push everything after it.
Complexity
- Time —
O(n) - Space —
O(n) for the output
Pitfalls
- Simulating second by second is
O(n²)on a long row of dots. - Forgetting the sentinels loses the leading and trailing runs.
- With an odd gap in
R…L, the centre domino stays'.'— it is not pushed either way.
Reference solution
Python
def pushDominoes(dominoes: str) -> str:
s = "L" + dominoes + "R"
out = []
prev = 0
for i in range(1, len(s)):
if s[i] == ".":
continue
gap = i - prev - 1
if prev > 0:
out.append(s[prev])
if s[prev] == s[i]:
out.append(s[i] * gap)
elif s[prev] == "L" and s[i] == "R":
out.append("." * gap)
else:
out.append("R" * (gap // 2))
if gap % 2 == 1:
out.append(".")
out.append("L" * (gap // 2))
prev = i
return "".join(out)JavaScript
var pushDominoes = function(dominoes) {
var s = "L" + dominoes + "R";
var out = [];
var prev = 0;
for (var i = 1; i < s.length; i++) {
if (s.charAt(i) === ".") continue;
var gap = i - prev - 1;
if (prev > 0) out.push(s.charAt(prev));
var t;
if (s.charAt(prev) === s.charAt(i)) {
for (t = 0; t < gap; t++) out.push(s.charAt(i));
} else if (s.charAt(prev) === "L" && s.charAt(i) === "R") {
for (t = 0; t < gap; t++) out.push(".");
} else {
var half = Math.floor(gap / 2);
for (t = 0; t < half; t++) out.push("R");
if (gap % 2 === 1) out.push(".");
for (t = 0; t < half; t++) out.push("L");
}
prev = i;
}
return out.join("");
};Also on the editorial tab: C, C#, C++, Go, Java, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.