给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例:
输入: "aab"
输出:
[
["aa","b"],
["a","a","b"]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-partitioning
解题思路
先利用动态规划给字符串中所有分割方式的子串是否是回文记录起来
dp[left][right] = s[left] == s[right] && (right - left <= 2 || dp[left + 1][right - 1])
对上式的解释:
- 当左右索引所在字符相等
- 并且
[left, right]
区间长度<= 3
或者[left + 1, right - 1]
区间的字符串是回文 - 上述两个条件满足时
[left, right]
区间的字符串为回文
解决判断回文,然后解决如何分割,采用深度优先搜索
将字符串分割映射过程看成一棵树,同一层节点在一个循环中分割,不同层的节点在递归中分割
代码
class Solution {
public List<List<String>> partition(String s) {
int n = s.length();
List<List<String>> result = new LinkedList<>();
if (n == 0) {
return result;
}
boolean[][] dp = new boolean[n][n];
initDP(s, dp);
LinkedList<String> path = new LinkedList<>();
dfs(s, dp, 0, path, result);
return result;
}
/**
* 深度优先遍历
*
* @param s 字符串
* @param dp 动态规划数组
* @param start 起始索引
* @param path 中间辅助变量(深度遍历过程的路径)
* @param result 结果集
* @return void
*/
private void dfs(String s, boolean[][] dp, int start, LinkedList<String> path, List<List<String>> result) {
if (start == s.length()) {
result.add(new ArrayList<>(path));
} else {
for (int i = start; i < s.length(); i++) {
// 截取s的[start, i]区间
if (dp[start][i]) { // 如果该区间是回文
String cur = s.substring(start, i + 1);
path.add(cur);
dfs(s, dp, i + 1, path, result);
path.removeLast();
}
}
}
}
/**
* 将字符串中索引[i, j]的子串是否为回文的结果保存到动态规划数组
*
* @param s 进行判断字符串
* @param dp 动态规划数组
* @return void
*/
private void initDP(String s, boolean[][] dp) {
for (int right = 0; right < s.length(); right++) {
for (int left = 0; left <= right; left++) {
// 当左右索引所在字符相等
// 并且[left, right]区间长度 <= 3 或者[left + 1, right - 1]区间的字符串是回文
// 上述两个条件满足时[left, right]区间的字符串为回文
if (s.charAt(left) == s.charAt(right) && (right - left <= 2 || dp[left + 1][right - 1])) {
dp[left][right] = true;
}
}
}
}
}