多米诺骨牌 | 豆包MarsCode AI刷题

131 阅读5分钟
# 问题描述

多米诺骨牌游戏规则非常简单,将骨牌按一定间距的尺寸排成单行,或分行排成一片。推倒第一张骨牌,其余发生连锁反应依次倒下,或形成一条长龙,或形成一幅图案。

小 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.."
}