# 问题描述
多米诺骨牌游戏规则非常简单,将骨牌按一定间距的尺寸排成单行,或分行排成一片。推倒第一张骨牌,其余发生连锁反应依次倒下,或形成一条长龙,或形成一幅图案。
小 A 觉得多米诺骨牌超级没意思,所以他想了点小花招。
小 A 将 n 个多米诺骨牌放在一条线上,每一块都垂直竖立。他同时将一些骨牌向左或向右推倒。注意:不会出现连续向左或者向右推的情况。 每过一秒,被推向左边或右边的骨牌会将左边或右边的相邻骨牌推倒。当一个骨牌,其左边倒向它的骨牌数目与其右边倒向它的骨牌数目相等时,由于力的平衡,该骨牌将依然保持竖立。
给定小 A 最初推骨牌的方向,求出最后依然保持竖立的骨牌数目和位置。
## 输入格式
输入数据第一行包括一个整数 n(1≤n≤3000),表示这一行多米诺骨牌的数目。下一行包括一个长度为 n 的字符串,字符串的第 i 个字符意义如下:
“L”,第 i 个字符将要被向左推。
“R”,第 i 个字符将要被向右推。
“.”,第 i 个字符不会被推。
## 输出格式
首先输出保持竖立的骨牌数目。如果保持竖立的骨牌数目不为 0,下一行输出保持竖立的骨牌的位置,骨牌位置从 1 到 n。
每两个数之间用一个空格隔开,注意最后一个数后面没有空格。
**输入样例**
14
.L.R...LR..L..
5
R....
1
.
**输出样例**
4
3 6 13 14
0
1
1
package main
import (
"fmt"
"strconv"
)
// 设置枚举类型代表当前的多种状态
const (
Monday int = iota // Monday = 0 初始状态,不知道会看到L还是R
Tuesday // Tuesday = 1 已经接收到L的状态,正在寻找R
Wednesday // Wednesday = 2 已经接收到R的状态,正在寻找L
)
func solution(num int, data string) string {
// Please write your code here
//使用变量储存状态
//使用一个切片储存还没有推到的骨牌
state := Monday
standing := make([]int, num)
tempRight := 0
for i := 0; i < num; i++ {
if data[i] == '.' {
if state == Wednesday && i == num-1 {
for j := tempRight; j <= i; j++ {
standing[j] = 1
}
}
} else if data[i] == 'L' {
if state == Monday {
for j := 0; j <= i; j++ {
standing[j] = 1
}
state = Tuesday
} else if state == Tuesday {
fmt.Println("error, L后面不应连续出现L")
} else {
//找到了R与L之间的区域,看看它们之间的距离是否为奇数,如果是的话就可以得出可以竖立的骨牌
for j := tempRight; j <= i; j++ {
standing[j] = 1
}
if (i-tempRight-1)%2 == 1 {
standing[tempRight+(i-tempRight-1)/2+1] = 0
}
//处理完中间区域后将状态转回Tuesday寻找R
state = Tuesday
}
} else {
tempRight = i
if state == Monday {
state = Wednesday
} else if state == Tuesday {
state = Wednesday
} else {
fmt.Println("error, R后面不应连续出现R")
}
}
}
//遍历standing计算0的个数,并输出结果
fmt.Println("standing 切片为", standing)
var result string
var count int = 0
for i := 0; i < num; i++ {
if standing[i] == 0 {
count++
}
}
if count == 0 {
return "0"
} else {
result = result + strconv.Itoa(count) + ":"
for i := 0; i < num; i++ {
if standing[i] == 0 {
result = result + strconv.Itoa(i+1) + ","
}
}
}
fmt.Println("result is ", result)
return result[:len(result)-1]
}
func main() {
// You can add more test cases here
fmt.Println(solution(14, ".L.R...LR..L..") == "4:3,6,13,14")
fmt.Println(solution(5, "R....") == "0")
fmt.Println(solution(1, ".") == "1:1")
}
这一题自己搓出来了,主要思路是单指针从左往右遍历,然后设置三个不同的状态分别处理遇到三种字符的情况,但是在leetcode上有进阶版,[https://leetcode.cn/problems/push-dominoes/description/](https://leetcode.cn/problems/push-dominoes/description/)
进阶版不再限定LR交替出现,其实在原有的思路上引入多一个指针,考虑两个指针之间的对比关系就能做出来,进阶版代码如下
class Solution {
public:
string pushDominoes(string dominoes) {
dominoes = "L" + dominoes + "R";
int l = 0;
string res = "";
for (int r = 1; r < dominoes.size(); ++r) {
if (dominoes[r] == '.') {
continue;
}
if (l != 0) { // 虚拟的牌不放入结果
res += dominoes[l];
}
int mid = r - l - 1;
if (dominoes[l] == dominoes[r]) {
res += string(mid, dominoes[l]);
} else if (dominoes[l] == 'L' && dominoes[r] == 'R') {
res += string(mid, '.');
} else {
res += string(mid / 2, 'R') + (mid % 2 == 1? "." : "") + string(mid / 2, 'L');
}
// cout << dominoes[l] << " " << dominoes[r] << " " << res << endl;
l = r;
}
return res;
}
};
将之改写为Go,上传后发现会超时 sad
func pushDominoes(dominoes string) string {
tempDominos := "L" + dominoes + "R"
//左指针
l := 0
var result string
for r := 1; r < len(tempDominos); r++ {
if tempDominos[r] == '.' {
//fmt.Println("Pass ", r)
continue
}
//在这已经找到一对区间了
if l != 0 {
result = result + string(tempDominos[l])
}
mid := r - l - 1
//如果左右相等,中间的都会变成L或者R
if tempDominos[l] == tempDominos[r] {
result = result + strings.Repeat(string(tempDominos[l]), mid)
//fmt.Println("first ", result)
} else if tempDominos[l] == 'L' && tempDominos[r] == 'R' {
result = result + strings.Repeat(".", mid)
//fmt.Println("second ", result)
} else {
result = result + strings.Repeat("R", mid/2)
if mid % 2 == 1 {
result = result + "."
}
result = result + strings.Repeat("L", mid/2)
//fmt.Println("third ", result)
}
l = r
}
return result
}
超时的原因是直接使用string拼接在GO是低效的,如果需要频繁操作字符串最好直接使用 <font style="color:#DF2A3F;">strings.Builder</font> 这里最好复习一下这玩意的使用。
package main
import (
"strings"
"fmt"
)
func pushDominoes(dominoes string) string {
tempDominos := "L" + dominoes + "R"
l := 0
var result strings.Builder
for r := 1; r < len(tempDominos); r++ {
if tempDominos[r] == '.' {
continue
}
// 计算两个非'.'字符之间的距离
mid := r - l - 1
// 将上一个非'.'字符加入结果
if l != 0 {
result.WriteByte(tempDominos[l])
}
// 根据左右字符的不同,处理区间中的字符
if tempDominos[l] == tempDominos[r] {
// 左右相同,全部填充为相同的字符
result.WriteString(strings.Repeat(string(tempDominos[l]), mid))
} else if tempDominos[l] == 'L' && tempDominos[r] == 'R' {
// 左右相对,一个'L',一个'R',中间填充'.'
result.WriteString(strings.Repeat(".", mid))
} else {
// 左右相对,一个'R',一个'L',填充对称的'R'和'L'
result.WriteString(strings.Repeat("R", mid/2))
if mid%2 == 1 {
result.WriteByte('.')
}
result.WriteString(strings.Repeat("L", mid/2))
}
// 更新左指针
l = r
}
return result.String()
}
func main() {
fmt.Println(pushDominoes(".L.R...LR..L..")) // 输出: "LL.RR.LLRRLL.."
}