[LeetCode] 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.

Given a string "S" representing the initial state. S[i] = 'L', if the i-th domino has been pushed to the left; S[i] = 'R', if the i-th domino has been pushed to the right; S[i] = '.', if the i-th domino has not been pushed.

Return a string representing the final state. 

Example 1:

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

Example 2:

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

Note:

  1. 0 <= N <= 10^5
  2. String dominoes contains only 'L', 'R' and '.'

 

這道題給我們擺好了一個多米諾骨牌陣列,但是與一般的玩法不同的是,這里沒有從一頭開始推,而是在很多不同的位置分別往兩個方向推,結果是骨牌各自向不同的方向倒下了,而且有的骨牌由於左右兩邊受力均等,依然屹立不倒,這樣的話骨牌就很難受了,能不能讓哥安心的倒下去?!生而為骨牌,總是要倒下去啊,就像漫天飛舞的櫻花,秒速五厘米的落下,回到最終歸宿泥土里。喂,不要給骨牌強行加戲好么!~ 某個位置的骨牌會不會倒,並且朝哪個方向倒,是由左右兩邊受到的力的大小決定的,那么可以分為下列四種情況:

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;
    }
};

 

類似題目:

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 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM