class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
}
};
140. 单词拆分 II
给定一个字符串 s
和一个字符串字典 wordDict
,在字符串 s
中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意:词典中的同一个单词可能在分段中被重复使用多次。
示例 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"] 输出:[]
提示:
1 <= s.length <= 20
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 10
s
和 wordDict[i]
仅有小写英文字母组成wordDict
中所有字符串都 不同原站题解
python3 解法, 执行用时: 48 ms, 内存消耗: 16 MB, 提交时间: 2023-06-05 14:16:40
class Solution: # 带备忘录的记忆化搜索 def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: res = [] memo = [1] * (len(s)+1) wordDict = set(wordDict) def dfs(wordDict,temp,pos): num = len(res) # 回溯前先记下答案中有多少个元素 if pos == len(s): res.append(" ".join(temp)) return for i in range(pos,len(s)+1): if memo[i] and s[pos:i] in wordDict: # 添加备忘录的判断条件 temp.append(s[pos:i]) dfs(wordDict,temp,i) temp.pop() # 答案中的元素没有增加,说明s[pos:]不能分割,修改备忘录 memo[pos] = 1 if len(res) > num else 0 dfs(wordDict,[],0) return res
java 解法, 执行用时: 1 ms, 内存消耗: 39.6 MB, 提交时间: 2023-06-05 14:16:02
import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; import java.util.List; import java.util.Set; public class Solution { public List<String> wordBreak(String s, List<String> wordDict) { // 为了快速判断一个单词是否在单词集合中,需要将它们加入哈希表 Set<String> wordSet = new HashSet<>(wordDict); int len = s.length(); // 第 1 步:动态规划计算是否有解 // dp[i] 表示「长度」为 i 的 s 前缀子串可以拆分成 wordDict 中的单词 // 长度包括 0 ,因此状态数组的长度为 len + 1 boolean[] dp = new boolean[len + 1]; // 0 这个值需要被后面的状态值参考,如果一个单词正好在 wordDict 中,dp[0] 设置成 true 是合理的 dp[0] = true; for (int right = 1; right <= len; right++) { // 如果单词集合中的单词长度都不长,从后向前遍历是更快的 for (int left = right - 1; left >= 0; left--) { // substring 不截取 s[right],dp[left] 的结果不包含 s[left] if (wordSet.contains(s.substring(left, right)) && dp[left]) { dp[right] = true; // 这个 break 很重要,一旦得到 dp[right] = True ,不必再计算下去 break; } } } // 第 2 步:回溯算法搜索所有符合条件的解 List<String> res = new ArrayList<>(); if (dp[len]) { Deque<String> path = new ArrayDeque<>(); dfs(s, len, wordSet, dp, path, res); return res; } return res; } /** * s[0:len) 如果可以拆分成 wordSet 中的单词,把递归求解的结果加入 res 中 * * @param s * @param len 长度为 len 的 s 的前缀子串 * @param wordSet 单词集合,已经加入哈希表 * @param dp 预处理得到的 dp 数组 * @param path 从叶子结点到根结点的路径 * @param res 保存所有结果的变量 */ private void dfs(String s, int len, Set<String> wordSet, boolean[] dp, Deque<String> path, List<String> res) { if (len == 0) { res.add(String.join(" ",path)); return; } // 可以拆分的左边界从 len - 1 依次枚举到 0 for (int i = len - 1; i >= 0; i--) { String suffix = s.substring(i, len); if (wordSet.contains(suffix) && dp[i]) { path.addFirst(suffix); dfs(s, i, wordSet, dp, path, res); path.removeFirst(); } } } }
javascript 解法, 执行用时: 56 ms, 内存消耗: 41.1 MB, 提交时间: 2023-06-05 14:15:28
/** * @param {string} s * @param {string[]} wordDict * @return {string[]} */ var wordBreak = function(s, wordDict) { const map = new Map(); const wordBreaks = backtrack(s, s.length, new Set(wordDict), 0, map); const breakList = []; for (const wordBreak of wordBreaks) { breakList.push(wordBreak.join(' ')); } return breakList; }; const backtrack = (s, length, wordSet, index, map) => { if (map.has(index)) { return map.get(index); } const wordBreaks = []; if (index === length) { wordBreaks.push([]); } for (let i = index + 1; i <= length; i++) { const word = s.substring(index, i); if (wordSet.has(word)) { const nextWordBreaks = backtrack(s, length, wordSet, i, map); for (const nextWordBreak of nextWordBreaks) { const wordBreak = [word, ...nextWordBreak] wordBreaks.push(wordBreak); } } } map.set(index, wordBreaks); return wordBreaks; }
golang 解法, 执行用时: 0 ms, 内存消耗: 2 MB, 提交时间: 2023-06-05 14:14:56
func wordBreak(s string, wordDict []string) (sentences []string) { wordSet := map[string]struct{}{} for _, w := range wordDict { wordSet[w] = struct{}{} } n := len(s) dp := make([][][]string, n) var backtrack func(index int) [][]string backtrack = func(index int) [][]string { if dp[index] != nil { return dp[index] } wordsList := [][]string{} for i := index + 1; i < n; i++ { word := s[index:i] if _, has := wordSet[word]; has { for _, nextWords := range backtrack(i) { wordsList = append(wordsList, append([]string{word}, nextWords...)) } } } word := s[index:] if _, has := wordSet[word]; has { wordsList = append(wordsList, []string{word}) } dp[index] = wordsList return wordsList } for _, words := range backtrack(0) { sentences = append(sentences, strings.Join(words, " ")) } return }
python3 解法, 执行用时: 48 ms, 内存消耗: 16.3 MB, 提交时间: 2023-06-05 14:14:42
class Solution: def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: @lru_cache(None) def backtrack(index: int) -> List[List[str]]: if index == len(s): return [[]] ans = list() for i in range(index + 1, len(s) + 1): word = s[index:i] if word in wordSet: nextWordBreaks = backtrack(i) for nextWordBreak in nextWordBreaks: ans.append(nextWordBreak.copy() + [word]) return ans wordSet = set(wordDict) breakList = backtrack(0) return [" ".join(words[::-1]) for words in breakList]