Topic
- Dynamic Programming
- Backtracking
- Depth-first Search
Description
https://leetcode.com/problems/palindrome-partitioning/
Given a string s
, partition s
such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s
.
A palindrome string is a string that reads the same backward as forward.
Example 1:
Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]
Example 2:
Input: s = "a"
Output: [["a"]]
Constraints:
1 <= s.length <= 16
s
contains only lowercase English letters.
Analysis
思路分析
本題涉及到兩個關鍵問題:
- 切割問題,有不同的切割方式
- 判斷迴文
切割問題類似組合問題。
例如對於字符串abcdef:
- 組合問題:選取一個a之後,在bcdef中再去選取第二個,選取b之後在cdef中在選取第三個.....。
- 切割問題:切割一個a之後,在bcdef中再去切割第二段,切割b之後在cdef中在切割第三段.....。
切割問題,也可以抽象爲一顆樹形結構,如圖:
遞歸用來縱向遍歷,for循環用來橫向遍歷,切割線(就是圖中的紅線)切割到字符串的結尾位置,說明找到了一個切割方法。
此時可以發現,切割問題的回溯搜索的過程和組合問題的回溯搜索的過程是差不多的。
判讀迴文
private boolean isPalindrome(String str, int startIndex, int endIndex) {
for(endIndex--; startIndex < endIndex; startIndex++, endIndex--) {
if(str.charAt(startIndex) != str.charAt(endIndex))
return false;
}
return true;
}
回溯三弄
函數簽名
List<string> path
:存放切割後迴文的子串。String originalString
:一開始傳入的字符串int startIndex
:子字符串開始下標。切割過的地方,不能重複切割,和組合問題也是保持一致的。List<list<string>> result
:存放結果集。
代碼如下:
private void backtracking(List<string> path, String originalString, //
int startIndex, List<list<string>> result) {}
終止條件
從上文樹形結構的圖中可以看出:切割線切到了字符串最後面,說明找到了一種切割方法,此時就是本層遞歸終止的終止條件。
代碼如下:
if(startIndex >= originalString.length()) {
result.add(new ArrayList<>(path));
return;
}
遍歷順序
首先判斷這個子串是不是迴文,如果是迴文,就加入在用來記錄切割過的迴文子串的path。
//i 表示終止截斷字符的下標(子串不含終止截斷字符)
for(int i = startIndex + 1; i <= originalString.length(); i++) {
if(isPalindrome(originalString, startIndex, i)) {
path.add(originalString.substring(startIndex, i));//加入[startIndex,i]在s中的子串
}else {
continue;
}
backtracking(path, originalString, i, result);// 尋找i+1爲起始位置的子串
path.remove(path.size() - 1);
}
最終代碼
移步至Submission
參考資料
Submission
public List<list<string>> partition(String s) {
List<string> path = new ArrayList<>();
List<list<string>> result = new ArrayList<>();
backtracking(path, s, 0, result);
return result;
}
private void backtracking(List<string> path, String originalString, //
int startIndex, List<list<string>> result) {
if(startIndex >= originalString.length()) {
result.add(new ArrayList<>(path));
return;
}
for(int i = startIndex + 1; i <= originalString.length(); i++) {
if(isPalindrome(originalString, startIndex, i)) {
path.add(originalString.substring(startIndex, i));
}else {
continue;
}
backtracking(path, originalString, i, result);
path.remove(path.size() - 1);
}
}
private boolean isPalindrome(String str, int startIndex, int endIndex) {
for(endIndex--; startIndex < endIndex; startIndex++, endIndex--) {
if(str.charAt(startIndex) != str.charAt(endIndex))
return false;
}
return true;
}
Test
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import org.junit.Test;
public class PalindromePartitioningTest {
@Test
@SuppressWarnings("unchecked")
public void test() {
PalindromePartitioning obj = new PalindromePartitioning();
List<list<string>> actual1 = obj.partition("a");
// System.out.println(actual1);
assertThat(actual1, //
containsInAnyOrder(Arrays.asList("a")));
List<list<string>> actual2 = obj.partition("aab");
// System.out.println(actual2);
assertThat(actual2, //
containsInAnyOrder(Arrays.asList("aa", "b"), Arrays.asList("a", "a", "b")));
}
}
</list<string></list<string></list<string></string></list<string></string></list<string></list<string></string></list<string></string>