給定字符串 s,將其分割成若干子串使每個子串都是回文。求最少分割次數。例如 "aab" → ["aa","b"] 需 1 次分割。
dp[i]:前 i 個字符(s[0..i-1])的最少分割數。
答案 = dp[n] - 1(n 個字符最多 n-1 次分割,初始 dp[0]=0)
dp[i] = min(dp[j] + 1) for all 0 ≤ j < i where s[j..i-1] 是回文
構建 isPal[i][j] 表示 s[i..j] 是否為回文。利用中心擴展或 DP 填表:
isPal[i][j] = (s[i]==s[j]) && (j-i<2 || isPal[i+1][j-1])
時間 O(n²),空間 O(n²),查詢 O(1)
public int minCut(String s) {
if (s == null || s.length() == 0) return 0;
boolean[][] dict = getDict(s);
int[] dp = new int[s.length() + 1];
dp[0] = 0;
for (int i = 0; i < s.length(); i++) {
dp[i+1] = i + 1; // 最壞情況:每字符獨立
for (int j = 0; j <= i; j++) {
if (dict[j][i]) {
dp[i+1] = Math.min(dp[i+1], dp[j] + 1);
}
}
}
return dp[s.length()] - 1;
}
private boolean[][] getDict(String s) {
boolean[][] dict = new boolean[s.length()][s.length()];
for (int i = s.length() - 1; i >= 0; i--) {
for (int j = i; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dict[i+1][j-1]))
dict[i][j] = true;
}
}
return dict;
}
| 問題 | 目標 | 方法 | 復雜度 |
|---|---|---|---|
| Palindrome Partitioning I | 所有分割方案 | 回溯 + 備忘錄 | 指數級(輸出敏感) |
| Word Break I | 能否分割 | DP O(n²) | 多項式 |
| Word Break II | 所有分割方案 | 回溯 + 備忘錄 | 指數級 |
| Palindrome Partitioning II | 最少分割數 | DP O(n²) | 多項式 |
dp[i] 定義為前 i 字符,比定義為以 i 結尾更便於轉移dp[i] = i 代表最壞情況(每字符獨立),保證 min 正確本題是「區間 DP + 預處理優化」的經典範例,展示了如何將看似 O(n³) 的問題通過空間換時間優化到 O(n²)。