🧩 题目描述
给定一个字符串 s
和一个字符串字典 wordDict
,在保证字符串可以被分割成若干个字典中的单词的前提下,返回所有可能的分割方式。
示例: 输入: s = "catsanddog" wordDict = ["cat", "cats", "and", "sand", "dog"]
输出: [ "cats and dog", "cat sand dog" ]
解题思路
两步策略:
- 动态规划(DP)+ 记录路径(prev) :判断某一位置是否可以被有效拆分,并记录所有有效拆分路径。
- 深度优先搜索(DFS)回溯 :通过 DFS 从
prev
中恢复所有路径,构造出最终结果。
🔢 算法实现步骤
1. 动态规划数组构建
-
定义:
dp[i]
:表示前i
个字符能否被字典中的单词有效切分。prev[j][i]
:若s[j..i-1]
是字典中的单词,并且dp[j]
为 true,则prev[j][i] = true
。
-
转移公式:
inifor (int i = 1; i <= n; i++) { for (int j = 0; j < i; j++) { if (dp[j] && wordDict.contains(s.substring(j, i))) { dp[i] = true; prev[j][i] = true; } } }
2. 回溯 DFS 构建句子
-
从末尾位置
n
开始向前回溯:- 若
prev[i][cur] == true
,则s[i..cur]
是有效单词。 - 将该单词加入路径
path
,递归向前继续查找。 - 到达起点
cur == 0
时,路径完整,反转 path 拼接成一个合法句子。
- 若
✅ Java 实现代码
ini
public class WordBreakII {
public List<String> wordBreakII(String s, List<String> wordDict) {
List<String> result = new ArrayList<>();
if (s == null || s.length() == 0 || wordDict == null || wordDict.isEmpty()) {
return result;
}
Set<String> set = new HashSet<>(wordDict);
int n = s.length();
boolean[] dp = new boolean[n + 1];
boolean[][] prev = new boolean[n][n + 1];
dp[0] = true;
// DP + 记录路径
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && set.contains(s.substring(j, i))) {
dp[i] = true;
prev[j][i] = true;
}
}
}
// 回溯构建路径
List<String> path = new ArrayList<>();
dfs(s, n, prev, path, result);
return result;
}
private void dfs(String s, int cur, boolean[][] prev, List<String> path, List<String> result) {
if (cur == 0) {
StringBuilder sb = new StringBuilder();
for (int i = path.size() - 1; i >= 0; i--) {
sb.append(path.get(i)).append(" ");
}
sb.deleteCharAt(sb.length() - 1);
result.add(sb.toString());
return;
}
for (int i = cur - 1; i >= 0; i--) {
if (prev[i][cur]) {
path.add(s.substring(i, cur));
dfs(s, i, prev, path, result);
path.remove(path.size() - 1);
}
}
}
}
📌 时间与空间复杂度分析
-
时间复杂度:
O(n^2)
- DP 过程:两重循环扫描子串。
- DFS:回溯树的最大分支与子问题数有关,最坏情况指数级。
-
空间复杂度:
O(n^2)
dp[]
、prev[][]
占用主要空间。
✨ 总结
- 将 可行性判断 与 路径记录 拆分,使代码结构清晰。
- 使用 二维布尔数组 prev[][] 来保存每一个可行的切分点,避免重复计算。
- 最后用 DFS 回溯路径,还原所有有效的句子。