文章目录
- 一、题目解析
- 二、问题分析与思考过程
-
- [2.1 回文串的定义](#2.1 回文串的定义)
- [2.2 思维导图](#2.2 思维导图)
- 三、解法一:回溯(DFS)
-
- [3.1 思路原理](#3.1 思路原理)
- [3.2 流程图示](#3.2 流程图示)
- [3.3 时间复杂度与空间复杂度分析](#3.3 时间复杂度与空间复杂度分析)
- [3.4 Java代码实现(基础版)](#3.4 Java代码实现(基础版))
- 四、解法二:预处理回文表(动态规划优化)
-
- [4.1 优化思路](#4.1 优化思路)
- [4.2 预处理 + 回溯流程图](#4.2 预处理 + 回溯流程图)
- [4.3 时间空间复杂度分析](#4.3 时间空间复杂度分析)
- [4.4 Java代码实现(优化版)](#4.4 Java代码实现(优化版))
- 五、总结与对比
一、题目解析
给定一个字符串 s,需要将其分割为若干子串,使得每个子串都是 回文串。返回所有可能的分割方案。
示例:
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
我们需要枚举所有可能的切分方式,只保留每个部分都是回文串的组合。
二、问题分析与思考过程
2.1 回文串的定义
一个字符串如果从前往后读与从后往前读完全相同,即为回文串。例如:
"aba"是回文串;"abc"不是回文串。
回文判断可以通过双指针实现:判断首尾字符是否相同,再逐步内缩。
2.2 思维导图
下面是题目的整体思考过程:
分隔回文串
回文判断
双指针判断是否为回文
可提前预处理
解法思路
回溯递归
从左到右枚举切分点
若前缀是回文 -> 递归剩余部分
动态规划
预处理回文判断表
提升效率
复杂度分析
时间复杂度较高(回溯)
可用DP优化回文检查
Java实现
递归 + 回溯
预处理优化
三、解法一:回溯(DFS)
3.1 思路原理
该题本质是枚举所有分割方式。
每次从字符串的开头开始,选择一个"切分点"作为当前子串的结束位置,如果该子串是回文,则递归处理剩下的部分。
核心思想:
- 用一个临时列表
path存当前的分割; - 当遍历到字符串末尾时,将一条完整方案加入结果集。
3.2 流程图示
是
否
是
否
开始
从索引 start 开始
start == s.length()?
加入当前方案到结果
循环 i 从 start 到 s.length()-1
是否为回文 s[start..i]?
加入到 path
递归调用 dfs(i+1)
回溯:移除最后一个元素
3.3 时间复杂度与空间复杂度分析
-
时间复杂度:
最坏情况下,每个字符都可能分割产生 (2^{n}) 种组合,且每次判断回文消耗 (O(n))。
预计复杂度为 O(n × 2ⁿ)。
-
空间复杂度:
递归深度最多为 (n),需要保存路径,复杂度为 O(n)。
3.4 Java代码实现(基础版)
java
import java.util.*;
public class Solution {
public List<List<String>> partition(String s) {
List<List<String>> res = new ArrayList<>();
dfs(s, 0, new ArrayList<>(), res);
return res;
}
private void dfs(String s, int start, List<String> path, List<List<String>> res) {
if (start == s.length()) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i < s.length(); i++) {
if (isPalindrome(s, start, i)) {
path.add(s.substring(start, i + 1));
dfs(s, i + 1, path, res);
path.remove(path.size() - 1); // 回溯
}
}
}
private boolean isPalindrome(String s, int l, int r) {
while (l < r) {
if (s.charAt(l++) != s.charAt(r--)) return false;
}
return true;
}
}
四、解法二:预处理回文表(动态规划优化)
4.1 优化思路
上面的方法中,每次都重复判断回文。
我们可以提前用一个二维数组 dp[i][j] 记录:
当 s[i..j] 是回文时,dp[i][j]=true。
状态转移:
d p [ i ] [ j ] = ( s [ i ] = = s [ j ] ) ∧ ( j − i < 3 或 d p [ i + 1 ] [ j − 1 ] ) dp[i][j] = (s[i] == s[j]) \land (j - i < 3 \text{ 或 } dp[i+1][j-1]) dp[i][j]=(s[i]==s[j])∧(j−i<3 或 dp[i+1][j−1])
这样,判断某个子串是否为回文只需 (O(1))。
4.2 预处理 + 回溯流程图
dp[i][j]==true
预处理 dp 数组
递归回溯 dfs
判断 s[i..j] 是否回文
加入方案
返回结果
4.3 时间空间复杂度分析
- 预处理:遍历全部子串,复杂度 (O(n^2))。
- 回溯仍然产生组合数 (O(2^n))。
总体复杂度:
时间:约 (O(n^2 + 2^n))
空间:(O(n^2))
4.4 Java代码实现(优化版)
java
import java.util.*;
public class Solution {
public List<List<String>> partition(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n];
// 预处理所有回文子串情况
for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
if (s.charAt(i) == s.charAt(j) &&
(j - i < 3 || dp[i + 1][j - 1])) {
dp[i][j] = true;
}
}
}
List<List<String>> res = new ArrayList<>();
dfs(s, 0, new ArrayList<>(), res, dp);
return res;
}
private void dfs(String s, int start, List<String> path, List<List<String>> res, boolean[][] dp) {
if (start == s.length()) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i < s.length(); i++) {
if (dp[start][i]) {
path.add(s.substring(start, i + 1));
dfs(s, i + 1, path, res, dp);
path.remove(path.size() - 1);
}
}
}
}
五、总结与对比
| 解法 | 回文判断方式 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|---|
| 回溯基础版 | 实时双指针判断 | O(n × 2ⁿ) | O(n) | 字符串较短时 |
| DP优化版 | 预处理回文表 | O(n² + 2ⁿ) | O(n²) | 大字符串场景 |