題目:
給定一個字符串 s,將 s 分割成一些子串,使每個子串都是迴文串。
返回 s 所有可能的分割方案。
示例:
輸入: "aab"
輸出:
[
["aa","b"],
["a","a","b"]
]
對於要求列出所有情況的,我們通常直接考慮使用回溯算法。需要在回溯的過程中加上一些小的技巧,例如通過條件排除一些情況等,來優化算法的時間複雜度。
對於本題,我們嘗試這樣思考:要得到所有都是迴文串的分割方案,我們就需要對字符串進行分割,至於需要分割幾下,我們不知道。那麼首先第一次分割的地方可以是第一個字符串之後,也可以是第二第三。。。個字符串之後,第二次分割是從上次分割之後的字符串開始,同樣長度可以是一二三。。。個字符串。這樣就能夠得出我們分割幾下取決於什麼時候碰到最後邊界。
這樣一想,這就是經典的回溯算法,最後的結果需要若干步驟才能得到,每一步又有很多個選擇,如果深入進去發現這條路不是最後的結果,那就退回來繼續嘗試其他的路徑。
解決本題的時候有一個關鍵技巧,就是在判斷子串是否是迴文串的時候,保存之前判斷的情況,這樣就能夠優化時間複雜度。
下面的代碼時間複雜度超過了100%的提交,代碼中添加了詳細註釋,結合上面的應該可以充分理解。
import java.util.ArrayList;
import java.util.List;
/**
* 回溯算法
* 時間複雜度:3ms,100%
* 空間複雜度:38.6MB,97.10%
*/
public class Solution_131 {
/*
用來保存從任意一位置至任意一位置的子串是否是迴文串,類似於動態規劃中保存之前的狀態來減小時間複雜度
不同之處在於這裏的狀態並沒有發生轉移,所以不算是動態規劃與回溯算法的結合
這一步是優化時間複雜度的關鍵
*/
int[][] dp;
public List<List<String>> partition(String s) {
List<List<String>> res = new ArrayList<>();
if (null == s || s.length() == 0) {
return res;
}
int length = s.length();
/*
它是一個二維矩陣,有三種狀態值:
0表示之前還沒有計算過
1表示從下表i到j的子串是迴文串
2表示不是迴文串
我們只用到了數組的右上半部分
當然這裏也可以使用char數組,空間複雜度更低
*/
dp = new int[length][length];
//初始化,單個字符的肯定是迴文串
for (int i = 0; i < length; i++) {
dp[i][i] = 1;
}
ArrayList<String> templist = new ArrayList<>();
helper(res, templist, s, length, 0);
return res;
}
/**
* 回溯算法
*
* @param res 結果集
* @param templist 中間list
* @param s 字符串
* @param length 字符串長度
* @param index 從當前位置向後組合判斷
*/
private void helper(List<List<String>> res, ArrayList<String> templist, String s, int length, int index) {
//走到這一步就表示走到了最後,添加到結果集
if (index == length) {
res.add(new ArrayList<>(templist));//一定要重新new一個對象,templist可以得到重用
}
//走到某一步有若干的選擇繼續走下一步
for (int i = index; i < length; i++) {
if (isPalindrome(s, index, i)) {
templist.add(s.substring(index, i + 1));
helper(res, templist, s, length, i + 1);
templist.remove(templist.size() - 1);//回溯算法中回退一定要記得這一步
}
}
}
//判斷是否是迴文串,這裏首先需要到保存的狀態中查看是否之前已經有了,優化時間複雜度
private boolean isPalindrome(String s, int from, int to) {
if (dp[from][to] == 1) {
return true;
} else if (dp[from][to] == 2) {
return false;
} else {
for (int i = from, j = to; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
dp[from][to] = 2;
return false;
}
}
dp[from][to] = 1;
return true;
}
}
/* public static void main(String[] args) {
List<List<String>> res = new Solution_131().partition("aab");
System.out.println(res);
}*/
}