每天一道算法题——推多米诺

目录

题目链接:838. 推多米诺 - 力扣(LeetCode)

题目描述

分析

解题思路 1:两次遍历(贪心 + 力传播)

核心思想:

🔧 实现步骤:

📌 示例说明:

✅ 解题思路 2:动态规划

🧠 核心思想:

✅ 总结

解法一:动态规划

Java写法:

C++写法:

运行时间

时间复杂度和空间复杂度

解法二:双遍历(贪心 + 力传播)

Java写法:

C++写法:

运行时间

时间复杂度和空间复杂度

总结


题目链接:838. 推多米诺 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

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 <= 10^5
  • dominoes[i] 为 'L''R' 或 '.'

分析

这个问题是一个经典的模拟类题目,关键在于如何高效地模拟多米诺骨牌倒下的过程。我们希望找出每个多米诺骨牌最终的状态(L、R 或 .)。下面是解决该问题的几种常见思路:


解题思路 1:两次遍历(贪心 + 力传播)

核心思想:

  • 每张未被推倒的多米诺骨牌会受到左右最近的“力”的影响。
  • 如果我们知道每个位置离它最近的左边的 R 和 最近的右边的 L 的距离,就可以判断它会被推向哪边。
  • 如果两个方向的力同时作用(距离相等),则保持直立。

🔧 实现步骤:

  1. 第一次从左到右扫描:
    • 记录每个位置被左边最近的 'R' 推动的时间(即距离)。
  2. 第二次从右到左扫描:
    • 记录每个位置被右边最近的 'L' 推动的时间(即距离)。
  3. 综合两个方向的力决定最终状态。

📌 示例说明:

输入:"RR.L"

  • 索引0: 'R',向右推
  • 索引1: 'R',向右推
  • 索引2: '.',受索引1的R影响,继续向右倒
  • 索引3: 'L'

最终输出:"RR.L"


✅ 解题思路 2:动态规划

🧠 核心思想:

  • 类似于双指针,但用 DP 数组记录每个位置最近的左/右施力点的距离。
  • 最后根据左右距离比较得出结果。

✅ 总结

方法时间复杂度空间复杂度是否推荐
双遍历法O(n)O(n)✅ 推荐,实现简单,效率高
BFS 法O(n)O(n)✅ 推荐,逻辑清晰
动态规划O(n)O(n)⭕ 可行,略复杂

解法一:动态规划

Java写法:

class Solution {
    public String pushDominoes(String dominoes) {
        int n = dominoes.length();
        int[] leftForce = new int[n];
        int force = 0;

        // 从左向右计算右向力
        for (int i = 0; i < n; i++) {
            if (dominoes.charAt(i) == 'R') {
                force = n; // 表示很强的右向力
            } else if (dominoes.charAt(i) == 'L') {
                force = 0; // 遇到L,清除右向力
            } else {
                force = Math.max(force - 1, 0);
            }
            leftForce[i] = force;
        }

        // 从右向左计算左向力,并比较决定最终方向
        force = 0;
        StringBuilder res = new StringBuilder();

        for (int i = n - 1; i >= 0; i--) {
            if (dominoes.charAt(i) == 'L') {
                force = n;
            } else if (dominoes.charAt(i) == 'R') {
                force = 0;
            } else {
                force = Math.max(force - 1, 0);
            }

            int diff = leftForce[i] - force;
            if (diff > 0) {
                res.append('R');
            } else if (diff < 0) {
                res.append('L');
            } else {
                res.append('.');
            }
        }

        return res.reverse().toString();
    }
}

C++写法:

#include <string>
#include <vector>
#include <algorithm>

class Solution {
public:
    std::string pushDominoes(std::string dominoes) {
        int n = dominoes.size();
        const int INF = n + 1;
        std::vector<int> left(n, INF), right(n, INF);

        // 从左到右计算右侧力
        int force = INF;
        for (int i = 0; i < n; ++i) {
            if (dominoes[i] == 'R') {
                force = 0;
            } else if (dominoes[i] == 'L') {
                force = INF;
            } else {
                force = force == INF ? INF : force + 1;
            }
            right[i] = force;
        }

        // 从右到左计算左侧力
        force = INF;
        for (int i = n - 1; i >= 0; --i) {
            if (dominoes[i] == 'L') {
                force = 0;
            } else if (dominoes[i] == 'R') {
                force = INF;
            } else {
                force = force == INF ? INF : force + 1;
            }
            left[i] = force;
        }

        // 构造最终结果
        for (int i = 0; i < n; ++i) {
            if (right[i] < left[i]) {
                dominoes[i] = 'R';
            } else if (left[i] < right[i]) {
                dominoes[i] = 'L';
            } else {
                dominoes[i] = '.';
            }
        }

        return dominoes;
    }
};

运行时间

时间复杂度和空间复杂度

  • 时间复杂度: O(n),同样地,我们也进行了三次线性扫描,分别用于初始化、从左到右计算以及从右到左计算。
  • 空间复杂度: O(n),使用了两个辅助数组来存储每个位置的左右力值。



解法二:双遍历(贪心 + 力传播)

Java写法:

class Solution {
    public String pushDominoes(String dominoes) {
        int n = dominoes.length();
        char[] d = dominoes.toCharArray();
        int[] rightForce = new int[n];
        int[] leftForce = new int[n];

        // 从左到右计算右边方向的力
        int force = 0;
        for (int i = 0; i < n; i++) {
            if (d[i] == 'R') {
                force = n;  // 最强向右力
            } else if (d[i] == 'L') {
                force = 0;  // 遇到L则清除向右力
            } else {
                force = Math.max(force - 1, 0);
            }
            rightForce[i] = force;
        }

        // 从右到左计算左边方向的力
        force = 0;
        for (int i = n - 1; i >= 0; i--) {
            if (d[i] == 'L') {
                force = n;  // 最强向左力
            } else if (d[i] == 'R') {
                force = 0;  // 遇到R则清除向左力
            } else {
                force = Math.max(force - 1, 0);
            }
            leftForce[i] = force;
        }

        // 比较左右力决定结果
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < n; i++) {
            if (rightForce[i] > leftForce[i]) {
                res.append('R');
            } else if (rightForce[i] < leftForce[i]) {
                res.append('L');
            } else {
                res.append('.');
            }
        }

        return res.toString();
    }
}

C++写法:

#include <string>
#include <vector>
#include <algorithm>

class Solution {
public:
    std::string pushDominoes(std::string dominoes) {
        int n = dominoes.size();
        std::vector<int> rightForce(n, 0);
        std::vector<int> leftForce(n, 0);

        // 计算右侧力
        int force = 0;
        for (int i = 0; i < n; ++i) {
            if (dominoes[i] == 'R') {
                force = n; // 强右向力
            } else if (dominoes[i] == 'L') {
                force = 0; // 清除右向力
            } else {
                force = std::max(force - 1, 0);
            }
            rightForce[i] = force;
        }

        // 计算左侧力
        force = 0;
        for (int i = n - 1; i >= 0; --i) {
            if (dominoes[i] == 'L') {
                force = n; // 强左向力
            } else if (dominoes[i] == 'R') {
                force = 0; // 清除左向力
            } else {
                force = std::max(force - 1, 0);
            }
            leftForce[i] = force;
        }

        // 根据左右力决定最终状态
        for (int i = 0; i < n; ++i) {
            if (rightForce[i] > leftForce[i]) {
                dominoes[i] = 'R';
            } else if (leftForce[i] > rightForce[i]) {
                dominoes[i] = 'L';
            } else {
                dominoes[i] = '.';
            }
        }

        return dominoes;
    }
};

运行时间

时间复杂度和空间复杂度

  • 时间复杂度: O(n),其中 n 是字符串 dominoes 的长度。我们对字符串进行了三次线性扫描。
  • 空间复杂度: O(n),我们需要额外的空间来存储左右两边的力值数组。

总结

        奥耶~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值