[LeetCode] 777. Swap Adjacent in LR String 交換LR字符串中的相鄰項


 

In a string composed of 'L''R', and 'X' characters, like "RXXLRXRXL", a move consists of either replacing one occurrence of "XL" with "LX", or replacing one occurrence of "RX" with "XR". Given the starting string start and the ending string end, return True if and only if there exists a sequence of moves to transform one string to the other.

Example:

Input: start = "RXXLRXRXL", end = "XRLXXRRLX"
Output: True
Explanation:
We can transform start to end following these steps:
RXXLRXRXL ->
XRXLRXRXL ->
XRLXRXRXL ->
XRLXXRRXL ->
XRLXXRRLX

Note:

  1. 1 <= len(start) = len(end) <= 10000.
  2. Both start and end will only consist of characters in {'L', 'R', 'X'}.

 

這道題給了我們一個只含有L,R,X三個字符的字符串,然后說有兩種操作,一種是把 "XL" 變成 "LX",另一種是把 "RX" 變成 "XR"。博主剛開始沒有讀題意,以為二者是可以互換的,錯誤的認為認為 "LX" 也能變成 "XL",其實題目這種變換是單向,這種單向關系就是解題的關鍵,具體來說,就是要把 start 字符串變成 end 字符串的話,L只能往前移動,因為是把 "XL" 變成 "LX",同樣,R只能往后移動,因為是把 "RX" 變成 "XR"。題目給的那個例子並不能很好的說明問題,博主之前那種雙向變換的錯誤認知會跪在這個例子:

start = "XXRXXLXXXX"
end  = "XXXXRXXLXX"

觀察這個 test case,可以發現 start 中的R可以往后移動,沒有問題,但是 start 中的L永遠無法變到end中L的位置,因為L只能往前移。這道題被歸類為 brainteaser,估計就是因為要觀察出這個規律吧。那么搞明白這個以后,其實可以用雙指針來解題,思路是,每次分別找到 start 和 end 中非X的字符,如果二者不相同的話,直接返回 false,想想問什么?這是因為不論是L還是R,其只能跟X交換位置,L和R之間是不能改變相對順序的,所以如果分別將 start 和 end 中所有的X去掉后的字符串不相等的話,那么就永遠無法讓 start 和 end 相等了。這個判斷完之后,就來驗證L只能前移,R只能后移這個限制條件吧,當i指向 start 中的L時,那么j指向 end 中的L必須要在前面,所以如果i小於j的話,就不對了,同理,當i指向 start 中的R,那么j指向 end 中的R必須在后面,所以i大於j就是錯的,最后別忘了i和j同時要自增1,不然死循環了。while 循環退出后,有可能i或j其中一個還未遍歷到結尾,而此時剩余到字符中是不能再出現L或R的,否則不能成功匹配,此時用兩個 while 循環分別將i或j遍歷完成,需要到了L或R直接返回 false 即可,加上了這一步后就不用在開頭檢測 start 和 end 中L和R的個數是否相同了,參見代碼如下:

 

解法一:

class Solution {
public:
    bool canTransform(string start, string end) {
        int n = start.size(), i = 0, j = 0;
        while (i < n && j < n) {
            while (i < n && start[i] == 'X') ++i;
            while (j < n && end[j] == 'X') ++j;
            if (start[i] != end[j]) return false;
            if ((start[i] == 'L' && i < j) || (start[i] == 'R' && i > j)) return false;
            ++i; ++j;
        }
        while (i < n) {
            if (start[i] != 'X') return false;
            ++i;
        }
        while (j < n) {
            if (end[j] != 'X') return false;
            ++j;
        }
        return true;
    }
};

 

下面這種解法也挺巧妙的,這里使用兩個計數器 cntL 和 cntR,分別來統計L和R出現的次數,統計方法時,start 中出現了L或R,計數器自增1,end 中出現了L或R,計數器自減1。注意檢測的順序很重要,由於 start 中的R必須在 end 中的R前面,所以要先檢測 start 中的R,同理,由於 end 中的L必須要在 start 中的L前面,所以要先檢測 end 中的L,那么四個 if 寫完后,如果 cntL 或者 cntR 中有任何一個小於0了,說明限制條件被打破了,返回 false,或者當二者都大於0的時候,說明此時不匹配了,參見上面解法中對於去掉所有的X的解釋,一個道理,說明L和R的相對順序不同了,那么也是 false。最終 for 循環退出后,如果 cntL 和 cntR 均為0的時候,才返回 true,否則就是 false,參見代碼如下:

 

解法二:

class Solution {
public:
    bool canTransform(string start, string end) {
        int n = start.size(), cntL = 0, cntR = 0;
        for (int i = 0; i < n; ++i) {
            if (start[i] == 'R') ++cntR;
            if (end[i] == 'L') ++cntL;
            if (start[i] == 'L') --cntL;
            if (end[i] == 'R') --cntR;
            if (cntL < 0 || cntR < 0 || cntL * cntR != 0) return false;
        }
        return cntL == 0 && cntR == 0;
    }
};

 

Github 同步地址:

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

 

參考資料:

https://leetcode.com/problems/swap-adjacent-in-lr-string/solution/

https://leetcode.com/problems/swap-adjacent-in-lr-string/discuss/126397/Simple-C++-solution-12ms-O(N)-time-and-O(1)-space

https://leetcode.com/problems/swap-adjacent-in-lr-string/discuss/113789/Simple-Java-one-pass-O(n)-solution-with-explaination

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

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



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