[LeetCode] 838. Push Dominoes 推多米诺骨牌

 

There are n dominoes in a line, and we place each domino vertically upright. In the beginning, we simultaneously push some of the dominoes either to the left or to the right.

After each second, each domino that is falling to the left pushes the adjacent domino on the left. Similarly, the dominoes falling to the right push their adjacent dominoes standing on the right.

When a vertical domino has dominoes falling on it from both sides, it stays still due to the balance of the forces.

For the purposes of this question, we will consider that a falling domino expends no additional force to a falling or already fallen domino.

You are given a string dominoes representing the initial state where:

  • dominoes[i] = 'L', if the ith domino has been pushed to the left,
  • dominoes[i] = 'R', if the ith domino has been pushed to the right, and
  • dominoes[i] = '.', if the ith domino has not been pushed.

Return a string representing the final state.

 

Example 1:

Input: dominoes = "RR.L"
Output: "RR.L"
Explanation: The first domino expends no additional force on the second domino.

Example 2:

Input: dominoes = ".L.R...LR..L.."
Output: "LL.RR.LLRRLL.."

 

Constraints:

  • n == dominoes.length
  • 1 <= n <= 105
  • dominoes[i] is either 'L''R', or '.'.

 

这道题给我们摆好了一个多米诺骨牌阵列,但是与一般的玩法不同的是,这里没有从一头开始推,而是在很多不同的位置分别往两个方向推,结果是骨牌各自向不同的方向倒下了,而且有的骨牌由于左右两边受力均等,依然屹立不倒,这样的话骨牌就很难受了,能不能让哥安心的倒下去?!生而为骨牌,总是要倒下去啊,就像漫天飞舞的樱花,秒速五厘米的落下,回到最终归宿泥土里。喂,不要给骨牌强行加戏好么?!~ 某个位置的骨牌会不会倒,并且朝哪个方向倒,是由左右两边受到的力的大小决定的,那么可以分为下列四种情况:

1)R....R  ->  RRRRRR

这是当两个向右推的操作连在一起时,那么中间的骨牌毫无悬念的都要向右边倒去。

2)L....L  ->  LLLLLL

当两个向左推的操作连在一起时,那么中间的骨牌毫无悬念的都要向左边倒去。

3)L....R  ->  L....R

当左边界的骨牌向左推,右边界的骨牌向右推,那么中间的骨牌不会收到力,所以依然保持坚挺。

4)R....L  -> RRRLLL   or   R.....L  ->  RRR.LLL

当左边界的骨牌向右推,右边界的骨牌向左推时,就要看中间的骨牌个数了,若是偶数,那么对半分,若是奇数,那么最中间的骨牌保持站立,其余的对半分。 

由于上述四种情况包含了所有的情况,所以目标就是在字符串中找出中间是‘点’的小区间,为了便于一次遍历就处理完,在 dominoes 字符串左边加个L,右边加个R,这并不会影响骨牌倒下的情况。这里使用双指针来遍历,其中i初始化为0,j初始化为1,当j指向‘点’时,就跳过,目标是i指向小区间的左边界,j指向右边界,然后用 j-i-1 算出中间‘点’的个数,为0表示中间没有点。若此时 i>0,则将左边界加入结果 res 中。若左右边界相同,那么中间的点都填成左边界,这是上述的情况一和二;若左边界是L,右边界是R,则是上述的情况三,中间还是保持点不变;若左边界是R,右边界是L,则是情况四,那么先加 mid/2 个R,再加 mid%2 个点,最后加 mid/2 个L即可。然后i更新为j,继续循环即可,参见代码如下:

 

解法一:

class Solution {
public:
    string pushDominoes(string dominoes) {
        string res = "";
        dominoes = "L" + dominoes + "R";
        for (int i = 0, j = 1; j < dominoes.size(); ++j) {
            if (dominoes[j] == '.') continue;
            int mid = j - i - 1;
            if (i > 0) res += dominoes[i];
            if (dominoes[i] == dominoes[j]) res += string(mid, dominoes[i]);
            else if (dominoes[i] == 'L' && dominoes[j] == 'R') res += string(mid, '.');
            else res += string(mid / 2, 'R') + string(mid % 2, '.') + string(mid / 2, 'L');
            i = j;
        }
        return res;
    }
};

 

下面这种解法遍历了两次字符串,第一次遍历是先把R后面的点全变成R,同时累加一个 cnt 数组,其中 cnt[i] 表示在 dominoes 数组中i位置时R连续出现的个数,那么拿题目中的例子1来说,第一次遍历之后,原 dominoes 数组,修改后的 dominoes 数组,以及 cnt 数组分别为:

.L.R...LR..L..
.L.RRRRLRRRL..
00001230012000

可以发现 cnt 数字记录的是R连续出现的个数,第一次遍历只模拟了所有往右推倒的情况,很明显不是最终答案,因为还需要往左推,那么就要把某些点变成L,已经把某些R变成点或者L,这时的 cnt 数组就非常重要,因为它相当于记录了往右推的 force 的大小。第二次遍历是从右往左,找所有L前面的位置,若其为点,则直接变为L。若其为R,那么也有可能变L,此时就要计算往左的 force,通过 cnt[i+1] + 1 获得,然后跟往右的 force 比较,若此位置往右的 force 大,说明当前骨牌应该往左倒,更新此时 cnt[i] 为往左的 force。若此时左右 force 相等了,说明当前骨牌不会向任意一遍倒,改为点即可,最终修改后的 dominoes 数组和 cnt 数组分别为:

LL.RR.LLRRLL..
10001210011000

 

解法二:

class Solution {
public:
    string pushDominoes(string dominoes) {
        int n = dominoes.size();
        vector<int> cnt(n);
        for (int i = 1; i < n; ++i) {
            if (dominoes[i - 1] == 'R' && dominoes[i] == '.') {
                dominoes[i] = 'R';
                cnt[i] = cnt[i - 1] + 1;
            }
        }
        for (int i = n - 2, cur = 0; i >= 0; --i) {
            if (dominoes[i + 1] != 'L') continue;
            cur = cnt[i + 1] + 1;
            if (dominoes[i] == '.' || cnt[i] > cur) {
                dominoes[i] = 'L';
                cnt[i] = cur;
            } else if (dominoes[i] == 'R' && cnt[i] == cur) {
                dominoes[i] = '.';
            }
        }
        return dominoes;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/838

 

类似题目:

Shortest Distance to a Character

 

参考资料:

https://leetcode.com/problems/push-dominoes/

https://leetcode.com/problems/push-dominoes/discuss/132332/C%2B%2BJavaPython-Two-Pointers

https://leetcode.com/problems/push-dominoes/discuss/132932/C%2B%2B-2-pass-scan-O(2N)-13ms

 

LeetCode All in One 题目讲解汇总(持续更新中...)

posted @ 2019-02-17 23:57  Grandyang  阅读(3954)  评论(3编辑  收藏  举报
Fork me on GitHub