问题描述
- Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s. - Example :
Input: “aab”
Output:
[
[“aa”,”b”],
[“a”,”a”,”b”]
]
问题分析
- 拆分一个给定的字符串,使得拆分后的所有子串都是回文串,返回所有拆分的可能。
- 这是得到枚举所有可能性类型的题目,所以要用 DFS + 回溯 的方法。对当前决策枚举出所有可能性(可走的路径),然后对每一种可能性进行 DFS。每当走完一种可能性,必须回溯到先前的状态,再走另一种可能性。
- 但该题还有个知识点便是回文串的判断涉及到动态规划, 比如对于 aabccb,如果我们已经找到 [a, a, bccd]后,再进行[aa,bccd]的查找时,会对 bccd 造成重复的是否回文的判断,所以如果我们利用动态规划找到 [i…j]所有的情况,存入表中,在dfs判断回文的时候,进行查表即可,不必重复判断。
经验教训
- 若涉及到枚举所有可能性,那么便是 DFS + 回溯
- 如何利用动态规划,填好
isPalindrome
那张表。
代码实现
class Solution {
public List<List<String>> partition(String s) {
if (s == null) {
return new ArrayList<>();
}
char[] chs = s.toCharArray();
ArrayList<String> list = new ArrayList<>();
//根据动态规划,填好isPalindrome这个二维表,isPalindrome[i][j] 表示 chs[i~j]是否是回文串
boolean[][] isPalindrome = setPalindromeMap(chs);
List<List<String>> res = new ArrayList<>();
findPalindrome(chs, 0, list, res, isPalindrome);
return res;
}
//[0 ~ i - 1]已经决策完成,形成了list,继续决策 [i ~ length - 1],若到了末尾,则加入结果集中
public void findPalindrome(char[] chs, int i, ArrayList<String> list, List<List<String>> res, boolean[][] isPalindrome) {
if (i == chs.length) {
//添加的是副本
res.add(new ArrayList<>(list));
}
//看从i开始,一直到末尾,当前若能得到回文串,则继续dfs
for (int k = i; k < chs.length; k++) {
if (isPalindrome[i][k]) { //chs[i ~ k] 是回文
list.add(new String(chs, i, k - i + 1)); //计入list
findPalindrome(chs, k + 1, list, res, isPalindrome); //继续dfs
//回溯!!!
list.remove(list.size() - 1);
}
}
return;
}
//根据动态规划,填好isPalindrome这个二维表,isPalindrome[i][j] 表示 chs[i~j]是否是回文串
public boolean[][] setPalindromeMap(char[] chs) {
boolean[][] isPalindrome = new boolean[chs.length][chs.length];
//只填右上半部分即可,dp[i][j] 依赖于 dp[i + 1][j - 1]
//若所依赖的dp[i + 1][j - 1] 不在右上半部分(i+1 > j - 1) , 直接返回true
for (int i = chs.length - 1; i >= 0; i--) {
isPalindrome[i][i] = true;
for(int j = i + 1; j < chs.length; j++) {
if (chs[i] == chs[j]) {
isPalindrome[i][j] = i+1 > j - 1 || isPalindrome[i + 1][j - 1];
}
}
}
return isPalindrome;
}
//动态规划初始递归思想
public boolean isPalindrome(char[] chs, int begin, int end) {
if (begin >= end) {
return true;
}
if (chs[begin] == chs[end]) {
return isPalindrome(chs, begin + 1, end - 1);
}
return false;
/*
while(begin < end) {
if(chs[begin] != chs[end]) {
return false;
}
++begin;
--end;
}
return true;
*/
}
}