Redian新闻
>
LeetCode 力扣官方题解 | 838. 推多米诺

LeetCode 力扣官方题解 | 838. 推多米诺

公众号新闻

838. 推多米诺(点击文末阅读原文查看题)

题目描述

n 张多米诺骨牌排成一行,将每张多米诺骨牌垂直竖立。在开始时,同时把一些多米诺骨牌向左或向右推。


每过一秒,倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌。同样地,倒向右边的多米诺骨牌也会推动竖立在其右侧的相邻多米诺骨牌。


如果一张垂直竖立的多米诺骨牌的两侧同时有多米诺骨牌倒下时,由于受力平衡, 该骨牌仍然保持不变。


就这个问题而言,我们会认为一张正在倒下的多米诺骨牌不会对其它正在倒下或已经倒下的多米诺骨牌施加额外的力。


给你一个字符串 dominoes 表示这一行多米诺骨牌的初始状态,其中:


  • dominoes[i] = 'L',表示第 i 张多米诺骨牌被推向左侧,

  • dominoes[i] = 'R',表示第 i 张多米诺骨牌被推向右侧,

  • dominoes[i] = '.',表示没有推动第 i 张多米诺骨牌。

返回表示最终状态的字符串。


示例 1:

输入:dominoes = "RR.L"输出:"RR.L"解释:第一张多米诺骨牌没有给第二张施加额外的力。


示例 2:

输入:dominoes = ".L.R...LR..L.."输出:"LL.RR.LLRRLL.."


提示:

  • n == dominoes.length

  • 1 <= n <= 105

  • dominoes[i] 为 'L'、'R' 或 '.'


解决方案


方法一:广度优先搜索


思路


当时间为 0 时,部分骨牌会受到一个初始的向左或向右的力而翻倒。过了 1 秒后,这些翻倒的骨牌会对其周围的骨牌施加一个力。具体表现为:


  • 向左翻倒的骨牌,如果它有直立的左边紧邻的骨牌,则会对该直立的骨牌施加一个向左的力。


  • 向右翻倒的骨牌,如果它有直立的右边紧邻的骨牌,则会对该直立的骨牌施加一个向右的力。


接下去需要分析这些 1 秒时受力的骨牌的状态。如果仅受到单侧的力,它们会倒向单侧;如果受到两个力,则会保持平衡。再过 1秒后,这些新翻倒的骨牌又会对其他直立的骨牌施加力,而不会对正在翻倒或已经翻倒的骨牌施加力。


这样的思路类似于广度优先搜索。我们用一个队列 q 模拟搜索的顺序;数组 time 记录骨牌翻倒或者确定不翻倒的时间,翻倒的骨牌不会对正在翻倒或者已经翻倒的骨牌施加力;数组 force 记录骨牌受到的力,骨牌仅在受到单侧的力时会翻倒。



代码


Python3

class Solution:    def pushDominoes(self, dominoes: str) -> str:        n = len(dominoes)        q = deque()        time = [-1] * n        force = [[] for _ in range(n)]        for i, f in enumerate(dominoes):            if f != '.':                q.append(i)                time[i] = 0                force[i].append(f)
res = ['.'] * n while q: i = q.popleft() if len(force[i]) == 1: res[i] = f = force[i][0] ni = i - 1 if f == 'L' else i + 1 if 0 <= ni < n: t = time[i] if time[ni] == -1: q.append(ni) time[ni] = t + 1 force[ni].append(f) elif time[ni] == t + 1: force[ni].append(f)        return ''.join(res)


Java

class Solution {    public String pushDominoes(String dominoes) {        int n = dominoes.length();        Deque<Integer> queue = new ArrayDeque<Integer>();        int[] time = new int[n];        Arrays.fill(time, -1);        List<Character>[] force = new List[n];        for (int i = 0; i < n; i++) {            force[i] = new ArrayList<Character>();        }        for (int i = 0; i < n; i++) {            char f = dominoes.charAt(i);            if (f != '.') {                queue.offer(i);                time[i] = 0;                force[i].add(f);            }        }
char[] res = new char[n]; Arrays.fill(res, '.'); while (!queue.isEmpty()) { int i = queue.poll(); if (force[i].size() == 1) { char f = force[i].get(0); res[i] = f; int ni = f == 'L' ? i - 1 : i + 1; if (ni >= 0 && ni < n) { int t = time[i]; if (time[ni] == -1) { queue.offer(ni); time[ni] = t + 1; force[ni].add(f); } else if (time[ni] == t + 1) { force[ni].add(f); } } } } return new String(res); }}


C#

public class Solution {    public string PushDominoes(string dominoes) {        int n = dominoes.Length;        Queue<int> queue = new Queue<int>();        int[] time = new int[n];        Array.Fill(time, -1);        IList<char>[] force = new IList<char>[n];        for (int i = 0; i < n; i++) {            force[i] = new List<char>();        }        for (int i = 0; i < n; i++) {            char f = dominoes[i];            if (f != '.') {                queue.Enqueue(i);                time[i] = 0;                force[i].Add(f);            }        }
char[] res = new char[n]; Array.Fill(res, '.'); while (queue.Count > 0) { int i = queue.Dequeue(); if (force[i].Count == 1) { char f = force[i][0]; res[i] = f; int ni = f == 'L' ? i - 1 : i + 1; if (ni >= 0 && ni < n) { int t = time[i]; if (time[ni] == -1) { queue.Enqueue(ni); time[ni] = t + 1; force[ni].Add(f); } else if (time[ni] == t + 1) { force[ni].Add(f); } } } } return new string(res); }}


C++

class Solution {public:    string pushDominoes(string dominoes) {        int n = dominoes.size();        queue<int> q;        vector<int> time(n, - 1);        vector<string> force(n);        for (int i = 0; i < n; i++) {            if (dominoes[i] != '.') {                q.emplace(i);                time[i] = 0;                force[i].push_back(dominoes[i]);            }        }
string res(n, '.'); while (!q.empty()) { int i = q.front(); q.pop(); if (force[i].size() == 1) { char f = force[i][0]; res[i] = f; int ni = (f == 'L') ? (i - 1) : (i + 1); if (ni >= 0 and ni < n) { int t = time[i]; if (time[ni] == -1) { q.emplace(ni); time[ni] = t + 1; force[ni].push_back(f); } else if(time[ni] == t + 1) { force[ni].push_back(f); } } } } return res; }};


C

typedef struct StListNode {    int val;    struct StListNode * next;} StListNode;
typedef struct Queue{ struct StListNode * head; struct StListNode * tail; int length;} Queue;
bool isEmpty(const Queue * obj) { return obj->length == 0;}
int length(const Queue * obj) { return obj->length;}
bool initQueue(Queue * obj) { obj->head = NULL; obj->tail = NULL; obj->length = 0; return true;}
bool pushQueue(Queue * obj, int val) { StListNode * node = (StListNode *)malloc(sizeof(StListNode)); node->val = val; node->next = NULL; if (NULL == obj->head) { obj->head = node; obj->tail = node; } else { obj->tail->next = node; obj->tail = obj->tail->next; } obj->length++; return true;}
int front(const Queue * obj) { if (obj->length == 0) { return -1; } return obj->head->val;}
int popQueue(Queue * obj) { if (obj->length == 0) { return -1; } int res = obj->head->val; StListNode * node = obj->head; obj->head = obj->head->next; obj->length--; free(node); return res;}
char * pushDominoes(char * dominoes){ int n = strlen(dominoes); int * time = (int *)malloc(sizeof(int) * n); char * res = (char *)malloc(sizeof(char) * (n + 1)); Queue ** force = (Queue **)malloc(sizeof(Queue *) * n);
for (int i = 0; i < n; i++) { time[i] = -1; force[i] = (Queue *)malloc(sizeof(Queue)); initQueue(force[i]); res[i] = '.'; } res[n] = '\0'; Queue qu; initQueue(&qu); for (int i = 0; i < n; i++) { if (dominoes[i] != '.') { pushQueue(&qu, i); time[i] = 0; pushQueue(force[i], dominoes[i]); } }
while (!isEmpty(&qu)) { int i = popQueue(&qu); if (length(force[i]) == 1) { char f = front(force[i]); res[i] = f; int ni = (f == 'L') ? (i - 1) : (i + 1); if (ni >= 0 && ni < n) { int t = time[i]; if (time[ni] == -1) { pushQueue(&qu, ni); time[ni] = t + 1; pushQueue(force[ni], f); } else if(time[ni] == t + 1) { pushQueue(force[ni], f); } } } } /* free resource */ for (int i = 0; i < n; i++) { while (!isEmpty(force[i])) { popQueue(force[i]); } } return res;}


Golang

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            force[i] = append(force[i], byte(ch))        }    }
ans := bytes.Repeat([]byte{'.'}, n) for len(q) > 0 { i := q[0] q = q[1:] if len(force[i]) > 1 { continue } 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)}


复杂度分析


  • 时间复杂度:O(n),其中 n 是 dominoes 的长度。每个下标会最多被判断一次状态。

  • 空间复杂度:O(n)。队列和数组最多各包含 n 个元素


法二:模拟

思路

我们可以枚举所有连续的没有被推动的骨牌,根据这段骨牌的两边骨牌(如果有的话)的推倒方向决定这段骨牌的最终状态:
  • 如果两边的骨牌同向,那么这段连续的竖立骨牌会倒向同一方向。

  • 如果两边的骨牌相对,那么这段骨牌会向中间倒。

  • 如果两边的骨牌相反,那么这段骨牌会保持竖立。

特别地,如果左侧没有被推倒的骨牌,则假设存在一块向左倒的骨牌;如果右侧没有被推倒的骨牌,则假设存在一块向右倒的骨牌。这样的假设不会破坏骨牌的最终状态,并且边界情况也可以落入到上述三种情况中。

我们可以使用两个指针 i 和 j 来枚举所有连续的没有被推动的骨牌,left 和 right 表示两边骨牌的推倒方向。根据上述三种情况来计算骨牌的最终状态。


代码

Python3
class Solution:    def pushDominoes(self, dominoes: str) -> str:        s = list(dominoes)        n, i, left = len(s), 0, 'L'        while i < n:            j = i            while j < n and s[j] == '.':  # 找到一段连续的没有被推动的骨牌                j += 1            right = s[j] if j < n else 'R'            if left == right:  # 方向相同,那么这些竖立骨牌也会倒向同一方向                while i < j:                    s[i] = right                    i += 1            elif left == 'R' and right == 'L':  # 方向相对,那么就从两侧向中间倒                k = j - 1                while i < k:                    s[i] = 'R'                    s[k] = 'L'                    i += 1                    k -= 1            left = right            i = j + 1        return ''.join(s)


Java
class Solution {    public String pushDominoes(String dominoes) {        char[] s = dominoes.toCharArray();        int n = s.length, i = 0;        char left = 'L';        while (i < n) {            int j = i;            while (j < n && s[j] == '.') { // 找到一段连续的没有被推动的骨牌                j++;            }            char right = j < n ? s[j] : 'R';            if (left == right) { // 方向相同,那么这些竖立骨牌也会倒向同一方向                while (i < j) {                    s[i++] = right;                }            } else if (left == 'R' && right == 'L') { // 方向相对,那么就从两侧向中间倒                int k = j - 1;                while (i < k) {                    s[i++] = 'R';                    s[k--] = 'L';                }            }            left = right;            i = j + 1;        }        return new String(s);    }}


C#

public class Solution {    public string PushDominoes(string dominoes) {        char[] s = dominoes.ToCharArray();        int n = s.Length, i = 0;        char left = 'L';        while (i < n) {            int j = i;            while (j < n && s[j] == '.') { // 找到一段连续的没有被推动的骨牌                j++;            }            char right = j < n ? s[j] : 'R';            if (left == right) { // 方向相同,那么这些竖立骨牌也会倒向同一方向                while (i < j) {                    s[i++] = right;                }            } else if (left == 'R' && right == 'L') { // 方向相对,那么就从两侧向中间倒                int k = j - 1;                while (i < k) {                    s[i++] = 'R';                    s[k--] = 'L';                }            }            left = right;            i = j + 1;        }        return new string(s);    }}


C++

class Solution {public:    string pushDominoes(string dominoes) {        int n = dominoes.size(), i = 0;        char left = 'L';        while (i < n) {            int j = i;            while (j < n && dominoes[j] == '.') { // 找到一段连续的没有被推动的骨牌                j++;            }            char right = j < n ? dominoes[j] : 'R';            if (left == right) { // 方向相同,那么这些竖立骨牌也会倒向同一方向                while (i < j) {                    dominoes[i++] = right;                }            } else if (left == 'R' && right == 'L') { // 方向相对,那么就从两侧向中间倒                int k = j - 1;                while (i < k) {                    dominoes[i++] = 'R';                    dominoes[k--] = 'L';                }            }            left = right;            i = j + 1;        }        return dominoes;    }};

C

char * pushDominoes(char * dominoes) {    int n = strlen(dominoes), i = 0;    char left = 'L';    while (i < n) {        int j = i;        while (j < n && dominoes[j] == '.') { // 找到一段连续的没有被推动的骨牌            j++;        }        char right = j < n ? dominoes[j] : 'R';        if (left == right) { // 方向相同,那么这些竖立骨牌也会倒向同一方向            while (i < j) {                dominoes[i++] = right;            }        } else if (left == 'R' && right == 'L') { // 方向相对,那么就从两侧向中间倒            int k = j - 1;            while (i < k) {                dominoes[i++] = 'R';                dominoes[k--] = 'L';            }        }        left = right;        i = j + 1;    }    return dominoes;}


Golang

func pushDominoes(dominoes string) string {    s := []byte(dominoes)    i, n, left := 0, len(s), byte('L')    for i < n {        j := i        for j < n && s[j] == '.' { // 找到一段连续的没有被推动的骨牌            j++        }        right := byte('R')        if j < n {            right = s[j]        }        if left == right { // 方向相同,那么这些竖立骨牌也会倒向同一方向            for i < j {                s[i] = right                i++            }        } else if left == 'R' && right == 'L' { // 方向相对,那么就从两侧向中间倒            k := j - 1            for i < k {                s[i] = 'R'                s[k] = 'L'                i++                k--            }        }        left = right        i = j + 1    }    return string(s)}



JavaScript

var pushDominoes = function(dominoes) {    const s = [...dominoes];    let n = s.length, i = 0;    let left = 'L';    while (i < n) {        let j = i;        while (j < n && s[j] == '.') { // 找到一段连续的没有被推动的骨牌            j++;        }        const right = j < n ? s[j] : 'R';        if (left === right) { // 方向相同,那么这些竖立骨牌也会倒向同一方向            while (i < j) {                s[i++] = right;            }        } else if (left === 'R' && right === 'L') { // 方向相对,那么就从两侧向中间倒            let k = j - 1;            while (i < k) {                s[i++] = 'R';                s[k--] = 'L';            }        }        left = right;        i = j + 1;    }    return s.join('');};


复杂度分析

  • 时间复杂度:O(n),其中 n 是 dominoes 的长度。每个下标会最多会被访问和赋值各一次。

  • 空间复杂度:O(1) 或 O(n)。某些语言字符串不可变,需要 O(n) 的额外空间。


BY / 

本文作者:davidditao

编辑&版式:Irene

声明:本文归“力扣”版权所有,如需转载请联系。



点个在看,少个 bug

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
Xcode弃用Bitcode,导致应用体积大幅增加题都白刷了?Amazon导师亲自为你揭秘LeetCode刷题真相!力扣秋招全攻略|刷对题、刷好题、刷出好 Offer拉郎配LeetCode 力扣官方题解 | 969. 煎饼排序《西罗普郡一少年》:22: 街上传来士兵行进的声响LeetCode 力扣官方题解 | 1791. 找出星型图的中心节点《山居续忆》:第三章:三叔祖礼耕先生 (五)投稿延期 | IEEE ICC 2023(IEEE国际通信会议)Leetcode出题人,揭秘打工体验!LeetCode 力扣官方题解 | 917. 仅仅反转字母LeetCode 力扣官方题解 | 537. 复数乘法2022傅雷翻译出版奖获奖者揭晓 Le palmarès du Prix Fu Lei 2022 dévoilé发现了《Leetcode刷题指南》,我半年成功转码上岸LeetCode刷完200道题,我竟然收到了亚麻的拒信...力扣刷题技巧篇|程序员萌新如何高效刷题韩国踩踏事故现场,“人像多米诺骨牌一样倒下”丨已有4名中国公民遇难Careers Stalled, China’s Liberal Arts Grads Learn to CodeIEEE is Your Professional Home - 会员精选视频为你诠释“IEEE如何引领你的专业发展”另一种“推翻” VS Code 的尝试:JetBrains Fleet 现开放公测35岁被裁,刷LeetCode变成了自我感动...试物说vol.550| 头皮上药器,米诺地尔的灵魂搭档LeetCode 力扣官方题解 | 6. Z 字形变换天啊,用米诺地尔治脱发,竟然要掉这么多头发!挑战刷最少的题进大厂!不会刷leetcode的人,救星来了美联储加息“多米诺效应” 南加惊悚上演!房贷月付猛增47%,销售额暴跌!房地产“保交楼”加速推进,专项借款助推多地停工项目复工刷力扣题想要做到 Bug Free 需培养怎样的思维?如此的诗,你怎么看?商科留学生转码必备:Leetcode到底该怎么刷?力扣特别策划 | 挑战不可能的运算LeetCode 力扣官方题解 | 2016. 增量元素之间的最大差值后悔GitHub疯传!求职亚麻的留学生,请立即停止刷LeetCode!大S强烈推荐!Rogaine 培健/落健 米诺地尔生发泡沫7.2折!
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。