Skip to content

Commit 2b249b5

Browse files
committed
feat: add solutions to lc problem: No.0838
No.0838.Push Dominoes
1 parent b8dbbf9 commit 2b249b5

File tree

3 files changed

+196
-144
lines changed

3 files changed

+196
-144
lines changed

solution/0800-0899/0838.Push Dominoes/README.md

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,40 @@ tags:
6868

6969
<!-- solution:start -->
7070

71-
### 方法一
71+
### 方法一:多源 BFS
72+
73+
把所有初始受到推力的骨牌(`L` 或 `R`)视作 **源点**,它们会同时向外扩散各自的力。用队列按时间层级(0, 1, 2 …)进行 BFS:
74+
75+
1. **建模**
76+
77+
- `time[i]` 记录第 *i* 张骨牌第一次受力的时刻,`-1` 表示尚未受力。
78+
- `force[i]` 是一个长度可变的列表,存放该骨牌在同一时刻收到的方向(`'L'``'R'`)。
79+
- 初始时把所有 `L/R` 的下标压入队列,并将它们的时间置 0。
80+
81+
2. **扩散规则**
82+
83+
- 当弹出下标 *i* 时,若 `force[i]` 只有一个方向,骨牌就会倒向该方向 `f`
84+
- 设下一张骨牌下标为
85+
86+
$$
87+
j=\begin{cases}
88+
i-1,& f=L\\[2pt]
89+
i+1,& f=R
90+
\end{cases}
91+
$$
92+
93+
若 `0 ≤ j < n`
94+
95+
- 若 `time[j]==-1`,说明 *j* 从未受力,记录 `time[j]=time[i]+1` 并入队,同时把 `f` 写入 `force[j]`
96+
- 若 `time[j]==time[i]+1`,说明它在同一“下一刻”已受过另一股力,此时只把 `f` 追加到 `force[j]`,形成对冲;后续因 `len(force[j])==2`,它将保持竖直。
97+
98+
3. **终态判定**
99+
100+
- 队列清空后,所有 `force[i]` 长度为 1 的位置倒向对应方向;长度为 2 的位置保持 `.`。最终将字符数组拼接为答案。
101+
102+
最终相似字符串组的数量就是并查集中连通分量的数量。
103+
104+
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是骨牌的数量。
72105

73106
<!-- tabs:start -->
74107

@@ -242,44 +275,40 @@ func pushDominoes(dominoes string) string {
242275
```ts
243276
function pushDominoes(dominoes: string): string {
244277
const n = dominoes.length;
245-
const map = {
246-
L: -1,
247-
R: 1,
248-
'.': 0,
249-
};
250-
let ans = new Array(n).fill(0);
251-
let visited = new Array(n).fill(0);
252-
let queue = [];
253-
let depth = 1;
278+
const q: number[] = [];
279+
const time: number[] = Array(n).fill(-1);
280+
const force: string[][] = Array.from({ length: n }, () => []);
281+
254282
for (let i = 0; i < n; i++) {
255-
let cur = map[dominoes.charAt(i)];
256-
if (cur) {
257-
queue.push(i);
258-
visited[i] = depth;
259-
ans[i] = cur;
283+
const f = dominoes[i];
284+
if (f !== '.') {
285+
q.push(i);
286+
time[i] = 0;
287+
force[i].push(f);
260288
}
261289
}
262-
while (queue.length) {
263-
depth++;
264-
let nextLevel = [];
265-
for (let i of queue) {
266-
const dx = ans[i];
267-
let x = i + dx;
268-
if (x >= 0 && x < n && [0, depth].includes(visited[x])) {
269-
ans[x] += dx;
270-
visited[x] = depth;
271-
nextLevel.push(x);
290+
291+
const ans: string[] = Array(n).fill('.');
292+
let head = 0;
293+
while (head < q.length) {
294+
const i = q[head++];
295+
if (force[i].length === 1) {
296+
const f = force[i][0];
297+
ans[i] = f;
298+
const j = f === 'L' ? i - 1 : i + 1;
299+
if (j >= 0 && j < n) {
300+
const t = time[i];
301+
if (time[j] === -1) {
302+
q.push(j);
303+
time[j] = t + 1;
304+
force[j].push(f);
305+
} else if (time[j] === t + 1) {
306+
force[j].push(f);
307+
}
272308
}
273309
}
274-
queue = nextLevel;
275310
}
276-
return ans
277-
.map(d => {
278-
if (!d) return '.';
279-
else if (d < 0) return 'L';
280-
else return 'R';
281-
})
282-
.join('');
311+
return ans.join('');
283312
}
284313
```
285314

solution/0800-0899/0838.Push Dominoes/README_EN.md

Lines changed: 106 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ tags:
2929
<p>You are given a string <code>dominoes</code> representing the initial state where:</p>
3030

3131
<ul>
32-
<li><code>dominoes[i] = &#39;L&#39;</code>, if the <code>i<sup>th</sup></code> domino has been pushed to the left,</li>
33-
<li><code>dominoes[i] = &#39;R&#39;</code>, if the <code>i<sup>th</sup></code> domino has been pushed to the right, and</li>
34-
<li><code>dominoes[i] = &#39;.&#39;</code>, if the <code>i<sup>th</sup></code> domino has not been pushed.</li>
32+
<li><code>dominoes[i] = &#39;L&#39;</code>, if the <code>i<sup>th</sup></code> domino has been pushed to the left,</li>
33+
<li><code>dominoes[i] = &#39;R&#39;</code>, if the <code>i<sup>th</sup></code> domino has been pushed to the right, and</li>
34+
<li><code>dominoes[i] = &#39;.&#39;</code>, if the <code>i<sup>th</sup></code> domino has not been pushed.</li>
3535
</ul>
3636

3737
<p>Return <em>a string representing the final state</em>.</p>
@@ -56,9 +56,9 @@ tags:
5656
<p><strong>Constraints:</strong></p>
5757

5858
<ul>
59-
<li><code>n == dominoes.length</code></li>
60-
<li><code>1 &lt;= n &lt;= 10<sup>5</sup></code></li>
61-
<li><code>dominoes[i]</code> is either <code>&#39;L&#39;</code>, <code>&#39;R&#39;</code>, or <code>&#39;.&#39;</code>.</li>
59+
<li><code>n == dominoes.length</code></li>
60+
<li><code>1 &lt;= n &lt;= 10<sup>5</sup></code></li>
61+
<li><code>dominoes[i]</code> is either <code>&#39;L&#39;</code>, <code>&#39;R&#39;</code>, or <code>&#39;.&#39;</code>.</li>
6262
</ul>
6363

6464
<!-- description:end -->
@@ -67,7 +67,38 @@ tags:
6767

6868
<!-- solution:start -->
6969

70-
### Solution 1
70+
### Solution 1: Multi-Source BFS
71+
72+
Treat all dominoes initially pushed (`L` or `R`) as **sources**, which simultaneously propagate their forces outward. Use a queue to perform BFS layer by layer (0, 1, 2, ...):
73+
74+
1. **Modeling**
75+
76+
- `time[i]` records the first moment when the _i_-th domino is affected by a force, with `-1` indicating it has not been affected yet.
77+
- `force[i]` is a variable-length list that stores the directions (`'L'`, `'R'`) of forces acting on the domino at the same moment.
78+
- Initially, push all indices of `L/R` dominoes into the queue and set their `time` to 0.
79+
80+
2. **Propagation Rules**
81+
82+
- When dequeuing index _i_, if `force[i]` contains only one direction, the domino will fall in that direction `f`.
83+
- Let the index of the next domino be:
84+
85+
$$
86+
j=\begin{cases}
87+
i-1,& f=L\\[2pt]
88+
i+1,& f=R
89+
\end{cases}
90+
$$
91+
92+
If $0 \leq j < n$:
93+
94+
- If `time[j] == -1`, it means _j_ has not been affected yet. Record `time[j] = time[i] + 1`, enqueue it, and append `f` to `force[j]`.
95+
- If `time[j] == time[i] + 1`, it means _j_ has already been affected by another force at the same "next moment." In this case, append `f` to `force[j]`, causing a standoff. Subsequently, since `len(force[j]) == 2`, it will remain upright.
96+
97+
3. **Final State Determination**
98+
99+
- After the queue is emptied, all positions where `force[i]` has a length of 1 will fall in the corresponding direction, while positions with a length of 2 will remain as `.`. Finally, concatenate the character array to form the answer.
100+
101+
The complexity is $O(n)$, and the space complexity is $O(n)$, where $n$ is the number of dominoes.
71102

72103
<!-- tabs:start -->
73104

@@ -193,46 +224,46 @@ public:
193224

194225
```go
195226
func pushDominoes(dominoes string) string {
196-
n := len(dominoes)
197-
q := []int{}
198-
time := make([]int, n)
199-
for i := range time {
200-
time[i] = -1
201-
}
202-
force := make([][]byte, n)
203-
for i, c := range dominoes {
204-
if c != '.' {
205-
q = append(q, i)
206-
time[i] = 0
207-
force[i] = append(force[i], byte(c))
208-
}
209-
}
210-
211-
ans := bytes.Repeat([]byte{'.'}, n)
212-
for len(q) > 0 {
213-
i := q[0]
214-
q = q[1:]
215-
if len(force[i]) > 1 {
216-
continue
217-
}
218-
f := force[i][0]
219-
ans[i] = f
220-
j := i - 1
221-
if f == 'R' {
222-
j = i + 1
223-
}
224-
if 0 <= j && j < n {
225-
t := time[i]
226-
if time[j] == -1 {
227-
q = append(q, j)
228-
time[j] = t + 1
229-
force[j] = append(force[j], f)
230-
} else if time[j] == t+1 {
231-
force[j] = append(force[j], f)
232-
}
233-
}
234-
}
235-
return string(ans)
227+
n := len(dominoes)
228+
q := []int{}
229+
time := make([]int, n)
230+
for i := range time {
231+
time[i] = -1
232+
}
233+
force := make([][]byte, n)
234+
for i, c := range dominoes {
235+
if c != '.' {
236+
q = append(q, i)
237+
time[i] = 0
238+
force[i] = append(force[i], byte(c))
239+
}
240+
}
241+
242+
ans := bytes.Repeat([]byte{'.'}, n)
243+
for len(q) > 0 {
244+
i := q[0]
245+
q = q[1:]
246+
if len(force[i]) > 1 {
247+
continue
248+
}
249+
f := force[i][0]
250+
ans[i] = f
251+
j := i - 1
252+
if f == 'R' {
253+
j = i + 1
254+
}
255+
if 0 <= j && j < n {
256+
t := time[i]
257+
if time[j] == -1 {
258+
q = append(q, j)
259+
time[j] = t + 1
260+
force[j] = append(force[j], f)
261+
} else if time[j] == t+1 {
262+
force[j] = append(force[j], f)
263+
}
264+
}
265+
}
266+
return string(ans)
236267
}
237268
```
238269

@@ -241,44 +272,40 @@ func pushDominoes(dominoes string) string {
241272
```ts
242273
function pushDominoes(dominoes: string): string {
243274
const n = dominoes.length;
244-
const map = {
245-
L: -1,
246-
R: 1,
247-
'.': 0,
248-
};
249-
let ans = new Array(n).fill(0);
250-
let visited = new Array(n).fill(0);
251-
let queue = [];
252-
let depth = 1;
275+
const q: number[] = [];
276+
const time: number[] = Array(n).fill(-1);
277+
const force: string[][] = Array.from({ length: n }, () => []);
278+
253279
for (let i = 0; i < n; i++) {
254-
let cur = map[dominoes.charAt(i)];
255-
if (cur) {
256-
queue.push(i);
257-
visited[i] = depth;
258-
ans[i] = cur;
280+
const f = dominoes[i];
281+
if (f !== '.') {
282+
q.push(i);
283+
time[i] = 0;
284+
force[i].push(f);
259285
}
260286
}
261-
while (queue.length) {
262-
depth++;
263-
let nextLevel = [];
264-
for (let i of queue) {
265-
const dx = ans[i];
266-
let x = i + dx;
267-
if (x >= 0 && x < n && [0, depth].includes(visited[x])) {
268-
ans[x] += dx;
269-
visited[x] = depth;
270-
nextLevel.push(x);
287+
288+
const ans: string[] = Array(n).fill('.');
289+
let head = 0;
290+
while (head < q.length) {
291+
const i = q[head++];
292+
if (force[i].length === 1) {
293+
const f = force[i][0];
294+
ans[i] = f;
295+
const j = f === 'L' ? i - 1 : i + 1;
296+
if (j >= 0 && j < n) {
297+
const t = time[i];
298+
if (time[j] === -1) {
299+
q.push(j);
300+
time[j] = t + 1;
301+
force[j].push(f);
302+
} else if (time[j] === t + 1) {
303+
force[j].push(f);
304+
}
271305
}
272306
}
273-
queue = nextLevel;
274307
}
275-
return ans
276-
.map(d => {
277-
if (!d) return '.';
278-
else if (d < 0) return 'L';
279-
else return 'R';
280-
})
281-
.join('');
308+
return ans.join('');
282309
}
283310
```
284311

0 commit comments

Comments
 (0)