1. 题目
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的
句子。
说明:
分隔时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
输入:
s = “catsanddog”
wordDict = [“cat”, “cats”, “and”, “sand”, “dog”]
输出:
[
“cats and dog”,
“cat sand dog”
]
示例 2:
输入:
s = “pineapplepenapple”
wordDict = [“apple”, “pen”, “applepen”, “pine”, “pineapple”]
输出:
[
“pine apple pen apple”,
“pineapple pen apple”,
“pine applepen apple”
]
解释: 注意你可以重复使用字典中的单词。
示例 3:
输入:
s = “catsandog”
wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出:
[]
Related Topics 动态规划 回溯算法
👍 463 👎 0
2. 题解
2.1 解法1: DFS递归
递归函数:
- 作用: 不停切分字符串, 返回切分的结果
- 参数: 切分开始的下标
- 返回值: [“www”,“www”…]
- 终止条件: start >= s.length(), 说明start越界时,即剩余子串为"",切不出单词了,整个s串一路被切成单词了。结束递归,返回空串[""], 因为题目要求若无结果返回空
class Solution {
Set<String> set;
String s;
public List<String> wordBreak(String s, List<String> wordDict) {
this.s = s;
this.set = new HashSet<>(wordDict);
return dfs(0);
}
public List<String> dfs(int start) {
if (start >= s.length()) {
return new ArrayList<String>() {{
add("");
}};
}
List<String> ans = new ArrayList<>();
// 开始向下搜索
for (int i = start + 1; i <= s.length(); i++) {
String curr = s.substring(start, i);
if (set.contains(curr)) {
// 切分后面的字符串得到结果
List<String> restAns = dfs(i);
// 每次切分得到一种结果就将其加入到结果集中
for (String restAn : restAns) {
// 拼接一种结果, 加入结果集
if (!restAn.equals("")){
String temp=curr+" "+restAn;
ans.add(temp);
} else {
// 否则说明已经分割到结尾, 将当前分割的单词加入结果集
ans.add(curr);
}
}
}
}
return ans;
}
}
优化:
由于中间会产生大量重复计算, 可以用 map 或数组存储计算结果,数组索引为指针位置,值为子调用的结果。下次遇到相同的子问题时,直接返回 memo 中的缓存值,而不是落入重复的递归。
class Solution {
Set<String> set;
String s;
Map<Integer, List<String>> map;
public List<String> wordBreak(String s, List<String> wordDict) {
this.s = s;
this.set = new HashSet<>(wordDict);
map = new HashMap<>();
return dfs(0);
}
public List<String> dfs(int start) {
if (map.containsKey(start)) {
return map.get(start);
}
if (start >= s.length()) {
return new ArrayList<String>() {{
add("");
}};
}
List<String> ans = new ArrayList<>();
// 开始向下搜索
for (int i = start + 1; i <= s.length(); i++) {
String curr = s.substring(start, i);
if (set.contains(curr)) {
// 切分后面的字符串得到结果
List<String> restAns = dfs(i);
// 每次切分得到一种结果就将其加入到结果集中
for (String restAn : restAns) {
// 拼接一种结果, 加入结果集
if (!restAn.equals("")) {
String temp = curr + " " + restAn;
ans.add(temp);
} else {
// 否则说明已经分割到结尾, 将当前分割的单词加入结果集
ans.add(curr);
}
}
}
}
map.put(start, ans);
return ans;
}
}