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 <= len(start) = len(end) <= 10000
.- 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/