LeetCode 87 - Scramble String
題目
題目連結:https://leetcode.com/problems/scramble-string
給定一個字串 s1,將 s1 分成 x 和 y 兩段子字串,你可以決定是否將交換兩段子字串的順序,也就是 s1 = x + y,或是 s1 = y + x。接著再對 x, y 分別進行一樣的操作,直到字串都變成長度 1 為止。
現在給你另一個字串 s2,問是否 s1 可以透過上述的操作變成 s2。
範例說明
Example 1:
1  | Input: s1 = "great", s2 = "rgeat"  | 
Example 2:
1  | Input: s1 = "abcde", s2 = "caebd"  | 
Example 3:
1  | Input: s1 = "a", s2 = "a"  | 
想法
我們可以枚舉字串的切點,對於每個切點我們也枚舉交換兩個子字串或是不交換兩個子字串的情形。
最好實現枚舉的方法就是利用遞迴。所以我們可以假設 isScramble(s1, s2) 代表 s1 字串有沒有可能透過 scramble 操作變成 s2。
那麼枚舉所有的 s1 = x1 + y1 並且:
- 將 
s2字串分為s2 = x2 + y2且x1,x2等長(那麼y1,y2也會等長),則當isScramble(x1, x2) == true && isScramble(y1, y2) == true,那麼isScramble(s1, s2) = true。 - 將 
s2字串分為s2 = x2 + y2且x1,y2等長(那麼y1,x2也會等長),則當isScramble(x1, y2) == true && isScramble(y1, x2) == true,那麼isScramble(s1, s2) = true。 
若枚舉完上述所有情形,都無法使 isScramble(s1, s2) = true,則 isScramble(s1, s2) = false。
不過純枚舉所有情形的話,那時間複雜度可能會很慘(當然,因為 LeetCode 的測資都很小,所以不做優化還是會跑的很快),所以我們可以考慮以下兩點優化:
- 記憶化搜索。也就已經判別過的,我們就不再判別一次。
 - 剪枝。
- 已經確認當前的字串不可能符合。那就是當 
s1的字母組成與s2不同時,那不管怎麼交換順序,s1和s2是永遠都不可能會變成一樣的。 - 已經確認當前的字串符合。當 
s1 = s2,則可以確認isScramble(s1, s2) = true。 
 - 已經確認當前的字串不可能符合。那就是當 
 
實作
首先,l1, l2, len 代表要驗證 isScramble 的兩個子字串是 s1[l1, l1 + len - 1] 以及 s2[l2, l2 + len - 1]。
所以 dp[l1][l2][len] 代表子字串 s1[l1, l1 + len - 1], s2[l2, l2 + len - 1] 的 isScramble。
dp 陣列初始化為 -1,代表還不知道答案。
接著實作遞迴函數 solver,當 dp[l1][l2][len] != -1,則代表答案已經被求得,直接回傳 dp[l1][l2][len] 的答案。
否則可以先進行兩個剪枝,isSame 用來判別兩次字串是否相等。cnt 則用來判別兩字串的字母組成是否相同,cnt[0] 代表字母 a 的數量,cnt[1] 代表字母 b 的數量…,以此類推,在字串 s1 將字母用加的加入 cnt,字串 s2 則將字母用扣的加入 cnt,最後 cnt 內應該全部為 0 才代表字串的字母組成相同。
若 isSame == true 則回傳 true,並且要將答案紀錄在 dp 陣列中,因此可以簡單的寫成 return (dp[l1][l2][len] = true)。
若 cnt 中有任何一項不為 0,代表字母組成不同,則回傳 false。
最後,枚舉切點以及是否交換順序。i 代表字串 s1 = x1 + y1 或是 s1 = y1 + x1 的 x1 字串長度。
當 s1 = x1 + y1,則 s2 = x2 + y2 的 x2 長度等於 x1 長度等於 i,而 y1, y2 長度等於 s1 長度 len 減去 i,也就是 len - i,因此若 solver(l1, l2, i) == true && solver(l1 + i, l2 + i, len - i) == true,則回傳 true。
當 s1 = y1 + x1,則 s2 = x2 + y2 的 x2 長度等於 y1 長度等於 len - i,而 y2, x1 長度等於 i,因此若 solver(l1 + i, l2, len - i) == true && solver(l1, l2 + len - i, i) == true,則回傳 true。
最後,若都沒有符合的,則回傳 false。
程式碼
1  | /**  |