題目描述:給定兩個等長字串 s1 和 s2,判斷 s2 是否為 s1 的「Scramble」版本。Scramble 的規則是:將字串視為一棵二元樹,可以在任意非葉節點處交換左右子樹,重複此操作後得到的字串即為 s1 的 scramble 字串。
解題思路:這是一道經典的區間動態規劃問題,使用三維 DP 陣列(或記憶化搜尋)來解決。定義 dp[i][j][len] 表示 s1 從索引 i 開始長度為 len 的子串,是否能 scramble 成 s2 從索引 j 開始長度為 len 的子串。
對於長度為 len 的子串,我們嘗試所有可能的切割點 k(1 ≤ k < len),每個切割點有兩種情況:
只要任一切割點的任一情況成立,則 dp[i][j][len] = true。邊界條件為長度為 1 時,s1[i] == s2[j]。實作上使用 map<tuple<int,int,int>, bool> 做記憶化,避免重複計算。
時間複雜度:O(n^4) — 狀態空間為 O(n^3)(i, j 各 O(n),len 為 O(n)),每個狀態需枚舉 O(n) 個切割點,加上每次比較時做字元計數需 O(n),總計 O(n^4)。字串長度 n ≤ 30,故實際執行很快。
空間複雜度:O(n^3) — 記憶化表儲存 O(n^3) 個子問題結果,加上遞迴呼叫棧深度 O(n)。
1. Define memo map from (i, j, len) to bool
2. Call solve(s1, s2, 0, 0, n) where n = length of strings
3. In solve(s1, s2, i, j, len):
a. If len == 1, return s1[i] == s2[j]
b. If (i, j, len) is in memo, return memo[(i, j, len)]
c. Count characters in s1[i..i+len-1] and s2[j..j+len-1]; if they differ, store false and return
d. For k from 1 to len-1:
- No-swap check: solve(s1, s2, i, j, k) AND solve(s1, s2, i+k, j+k, len-k)
- Swap check: solve(s1, s2, i, j+len-k, k) AND solve(s1, s2, i+k, j, len-k)
- If either check is true, store true in memo and return true
e. Store false in memo and return false方法一:三維底層 DP(由短到長迭代) 建立 dp[len][i][j] 陣列,先初始化長度為 1 的子問題,再由 len=2 逐步擴展至 n。每個狀態轉移同遞迴版本。時間複雜度 O(n^4),空間複雜度 O(n^3),但無遞迴開銷,常數較小。
方法二:記憶化遞迴 + 字串 key 快取
使用 s1 的子串與 s2 的子串直接作為 map key(map<string, map<string, bool>>),可讀性更高但效能較差,因字串雜湊和比較的開銷增加。時間複雜度仍為 O(n^4),空間複雜度 O(n^3),但實際常數較大。