1. 程式人生 > >[LeetCode] Swap Adjacent in LR String 交換LR字串中的相鄰項

[LeetCode] 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  = "XXXXR

XXLXX"

我們觀察這個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,不然死迴圈了,參見程式碼如下:

解法一:

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

參考資料: