题目
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例:
输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ]
动态规划 + 回溯
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.partition("aab");
}
String s;
List<List<String>> ans = new ArrayList<>();
boolean [][] dpTable;
public List<List<String>> partition(String s) {
this.s = s;
// 首先利用动态规划 得到字符串中哪些字符串能够作为回文串
dpTable = new boolean[s.length()][s.length()];
for (int l = 0; l < s.length(); l ++) {
int left = 0;
int right = left + l;
while (right < s.length()) {
if (l == 0) {
dpTable[left][right] = Boolean.TRUE;
}
if (l == 1) {
dpTable[left][right] = s.charAt(left) == s.charAt(right);
}
if ( l > 1) {
dpTable[left][right] = dpTable[left + 1][right - 1] && (s.charAt(left) == s.charAt(right));
}
left ++;
right ++;
}
}
// 再利用回溯法得到所有可能的组合
huisu(new ArrayList<>(), 0);
return ans;
}
public void huisu(List<String> path, int start) {
if (start == s.length()) {
List<String> tt = new ArrayList<>();
tt.addAll(path);
ans.add(tt);
return;
}
// 选择列表为start开始的所有子串
for (int i = start; i < s.length(); i ++) {
if (dpTable[start][i]) {
// 当前子串是回文才有继续递归下去的必要
String temp = s.substring(start, i + 1);
path.add(temp);
// 注意继续切割字符串的开头, 是现在切割下来字符串的尾巴, 即应该传递i + 1
huisu(path, i + 1);
// 撤销选择
if (path.size() > 0) {
path.remove(path.size() - 1);
}
}
}
}
}
基本思路
-
动态规划可以用来判断i, j两个索引在原字符串中的该子串s.subString(i, j + 1)是否是回文串, 提高判断某个已知子串是否是回文的效率
-
除非是特殊情况, 否则让你给出所有结果的时候, 回溯法(或者dfs,bfs)是唯一的方法.
-
递归的选择是从字符串开头, 按照长度为1开始遍历切割剩下的字符串, 如果切割下来的字符串是回文, 才有继续切割的必要, 递归的条件就是传递起始位置