题目
原文链接:leetcode-cn.com/problems/pu…
读题
-
模拟多米诺骨牌的推倒过程
-
本题目中多个推倒同时发生
-
如果骨牌同时受左右两个方向的推到,则不会倒下
-
倒下或者正在倒下的骨牌不会相互影响
-
骨牌的推倒过程是以秒为单位的(这说明整个过程会有多个中间态)
-
题目要求输出最终的骨牌状态
解题
解题思路
- 根据题意,只有状态为“.”的骨牌会受到影响,因此可以遍历骨牌,对每个“.”状态的骨牌进行受力分析。
- 不断遍历,如果遍历中发现没有新的骨牌倒下,则说明推倒过程结束
go解答
package main
import (
"fmt"
)
func pushDominoes(dominoes string) string {
arr := []byte(dominoes)
l := len(arr)
if l == 1 {
return dominoes
}
// 在本方法中,arr[i-1]会被修改,因此需要提前记住
var left, current byte
for {
hasChange := false
for i := 0; i < l; i++ {
left = current
current = arr[i]
if arr[i] != '.' {
continue
}
if i == 0 && arr[1] == 'L' {
arr[i] = 'L'
hasChange = true
}
if i == l-1 && left == 'R' {
arr[i] = 'R'
hasChange = true
}
if 0 < i && i < l-1 {
if left == 'R' && arr[i+1] != 'L' {
arr[i] = 'R'
hasChange = true
} else if left != 'R' && arr[i+1] == 'L' {
arr[i] = 'L'
hasChange = true
}
}
}
if !hasChange {
break
}
}
return string(arr)
}
func main() {
s := "R.R.L"
fmt.Println(pushDominoes(s))
}
反思与总结
我上面的方法明显存在很多的改进空间,比如每次循环都会遍历整个数组,事实上可能某一部分已经处理好了,不用每次都去重复遍历。如果输入时这样的“R..........”,我这边的方法效率会很低,每次循环都只处理了一个骨牌。
官方解题方法中,第二种方法也是跟我使用一样的模拟思路,不过官方在细节上处理的更好。大概思路是找出一段R|L.......R|L
骨牌,进行批量处理。
func pushDominoes(dominoes string) string {
arr := []byte(dominoes)
i, n, left := 0, len(arr), byte('L')
for i < n {
// 跳过连续的LLRR段
if arr[i] != '.' {
left = arr[i]
i++
continue
}
j := i
for j < n && arr[j] == '.' {
j++
}
// left和right给个无副作用的初始值
right := byte('R')
if j < n {
right = arr[j]
}
// 方向一致
if left == right {
for i < j {
arr[i] = left
i++
}
// 方向相对,注意当i=k时,受力平衡无须处理
} else if left == 'R' && right == 'L' {
k := j - 1
for i < k {
arr[i] = 'R'
arr[k] = 'L'
i++
k--
}
}
left = right
i = j + 1
}
return string(arr)
}
除此之外,官方还使用了类似广度优先搜索来解决。
func pushDominoes(dominoes string) string {
n := len(dominoes)
q := []int{}
// 记录当前位置受到了第几秒的推力
time := make([]int, n)
for i := range time {
time[i] = -1 // 表示还没开始
}
force := make([][]byte, n) // 记录每个位置受到的推力数组,每个位置最多受到两个推力
for i, ch := range dominoes {
if ch != '.' {
q = append(q, i)
time[i] = 0 // 0表示最开始的推力
force[i] = append(force[i], byte(ch))
}
}
ans := bytes.Repeat([]byte{'.'}, n)
for len(q) > 0 {
i := q[0]
q = q[1:]
// 受到两个推力,无论时LR还是RL都最终对该位置无影响
if len(force[i]) > 1 {
continue
}
// 注意这里len(force[i]) == 1
f := force[i][0]
ans[i] = f
// 判断该推力会对左边还是右边的骨牌产生影响
ni := i - 1
if f == 'R' {
ni = i + 1
}
if 0 <= ni && ni < n {
t := time[i]
// 之前没有受到推力
if time[ni] == -1 {
q = append(q, ni)
time[ni] = t + 1
force[ni] = append(force[ni], f)
// 判断是否是同一秒的推力
} else if time[ni] == t+1 {
force[ni] = append(force[ni], f)
}
}
}
return string(ans)
}