題目描述:給定一個字串 s,將其分割成若干子字串,使得每個子字串都是回文串。回傳所有可能的分割方案。
解題思路:本題結合了回溯(Backtracking)與動態規劃(DP)預處理兩種技巧。
回溯框架:從位置 0 開始,嘗試所有以當前位置為起點的子字串 s[start..i];若它是回文串,則將其加入當前分割方案,並對剩餘的 s[i+1..] 遞迴處理。當 start 到達字串末尾時,代表找到一個完整的分割方案,記錄下來。
DP 預計算回文:每次回溯時若用雙指標逐字元判斷回文,最壞情況下每次判斷需 O(n)。為了加速,可預先用 O(n²) 的 DP 計算所有子字串是否為回文:
dp[i][j] = true 若 s[i..j] 是回文dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1](需按長度由短到長填表)這樣在回溯時,判斷任意子字串是否為回文只需 O(1)。
時間複雜度:O(n · 2^n) — 字串有 n-1 個分割點,每個分割點可以切或不切,最多 2^(n-1) 種分割方式;每種方式複製到結果需 O(n)。DP 預處理需 O(n²),被回溯的指數項主導。
空間複雜度:O(n²) — DP 表需要 n×n 的空間;遞迴堆疊深度最大為 n;current 陣列最多儲存 n 個子字串(最壞情況每個字元單獨一組)。不計輸出結果所用的空間。
1. Precompute DP table: dp[i][j] = true if s[i..j] is a palindrome
- For i from n-1 down to 0:
- For j from i to n-1:
- dp[i][j] = (s[i] == s[j]) AND (j-i <= 2 OR dp[i+1][j-1])
2. Define backtrack(start, current):
a. If start == n: add copy of current to result, return
b. For end from start to n-1:
- If dp[start][end] is true (s[start..end] is palindrome):
- Append s[start..end] to current
- Recurse: backtrack(end + 1, current)
- Remove last element from current
3. Call backtrack(0, [])
4. Return result回溯 + 內聯回文判斷 O(n · 2^n):省去 DP 預處理,直接在回溯時用雙指標判斷子字串是否為回文。程式碼更簡單,但每次判斷需 O(n),整體時間複雜度為 O(n² · 2^n),適合 n 較小的情況。
Manacher 演算法預處理 O(n):用 Manacher 演算法在 O(n) 時間內找出所有回文子字串的中心與半徑,從而 O(1) 判斷任意子字串是否為回文。這將預處理從 O(n²) 優化至 O(n),但整體瓶頸仍在回溯的 O(n · 2^n),對最終複雜度影響有限,且實作較複雜,通常 DP 方案已足夠。