There are N
dominoes in a line, and we place each domino vertically upright.html
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.dom
Similarly, the dominoes falling to the right push their adjacent dominoes standing on the right.ide
When a vertical domino has dominoes falling on it from both sides, it stays still due to the balance of the forces.post
For the purposes of this question, we will consider that a falling domino expends no additional force to a falling or already fallen domino.this
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.url
Return a string representing the final state. spa
Example 1:指针
Input: ".L.R...LR..L.." Output: "LL.RR.LLRRLL.."
Example 2:code
Input: "RR.L" Output: "RR.L" Explanation: The first domino expends no additional force on the second domino.
Note:
0 <= N <= 10^5
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