给定一个非空字符串 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"]
输出:
[]
请先翻阅 LeetCode 单词拆分
这道题不但要判断串是否可拆,还要把所有的情况列出来,对于这种题一般都是回溯法。
class Solution {
public:
set<int> wordLengthSet;//用于储存wordDict中单词的长度
vector<string> result;
vector<string> wordBreak(string &s, vector<string>& wordDict) {
if (s == "" || wordDict.size() == 0) {
return {};
}
//首先扫描一遍,获取wordDict长度的种类
for (auto word : wordDict) {
wordLengthSet.insert(word.size());
}
//对初始长度进行穷举
for (auto it = wordLengthSet.rbegin(); it != wordLengthSet.rend(); ++it) {
string subS = s.substr(0, *it);//以nowIndex为起始,获取*it长的子串
if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end()) {
dfs(wordDict, s, *it, subS);
}
}
return result;
}
void dfs(vector<string>& wordDict, string &str, int nowIndex, string tempRes) {
int strSize = str.size();
if (nowIndex >= strSize) {//此次拆分成功
result.push_back(tempRes);//保留结果
return;
}
//对长度进行穷举
for (auto it = wordLengthSet.rbegin(); it != wordLengthSet.rend(); ++it) {
string subS = str.substr(nowIndex, *it);//以nowIndex为起始,获取*it长的子串
if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end() && nowIndex + *it <= strSize) {
dfs(wordDict, str, nowIndex + *it, tempRes + " " + subS);
}
}
}
};
但是超时了。。。
优化思路:利用上一题的动态规划标记法,标记[i, s.size() - 1]是否可拆分,如果不可拆分就跳过。(剪枝)
class Solution {
public:
set<int> wordLengthSet;//用于储存wordDict中单词的长度
vector<string> wordBreak(string s, vector<string>& wordDict) {
vector<string> result;
string tempRes;
vector<bool> possible(s.size() + 1, true);//possible[i]用于标记[i, s.size() - 1]是否可拆,初始化都可拆
//首先扫描一遍,获取wordDict长度的种类
for (auto word : wordDict) {
wordLengthSet.insert(word.size());
}
wordBreakDFS(s, wordDict, 0, possible, tempRes, result);//开始搜索
return result;
}
void wordBreakDFS(string &s, vector<string> &wordDict, int start, vector<bool> &possible, string &tempRes, vector<string> &result) {
int strSize = s.size();
if (start == s.size()) {//此次搜索成功
result.push_back(tempRes.substr(0, tempRes.size() - 1));//需要去掉末尾的空格
return;
}
//对长度进行穷举
for (auto it = wordLengthSet.begin(); it != wordLengthSet.end() && start + *it <= strSize; ++it) {
string word = s.substr(start, *it);
//possible[start + *it + 1]为增加的判断条件,判断[0, start + *it]是否可拆
if (find(wordDict.begin(), wordDict.end(), word) != wordDict.end() && possible[start + *it + 1]) {
tempRes.append(word).append(" ");//添加word、空格
int oldSize = result.size();
wordBreakDFS(s, wordDict, start + *it, possible, tempRes, result);
if (result.size() == oldSize){//如果经过上面的dfs,结果数还未增加,说明后面这段不可拆
possible[start + *it + 1] = false;
}
tempRes.resize(tempRes.size() - word.size() - 1);//使用完后记得恢复tempRes字符串
}
}
}
};