Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:
Input:
s = “catsanddog”
wordDict = [“cat”, “cats”, “and”, “sand”, “dog”]
Output:
[
“cats and dog”,
“cat sand dog”
]
method 1 backtracking
附上一个TLE的反面案例
超时的原因在于,许多子字符串的分割重复,每次传入的都是原来的字符串,再加上一个start指示开始索引
一般情况下都是可以通过的,但当遇到极端case诸如 aaaaaaaaaaaaaaa和[a,aa,aaa,aaaa,aaaaa]这样的,会导致大量的重复计算,最后导致超时
对于超时有两点体会
- 引入dp
- 每次传入时,传入新的截取过后的字符串,更容易分析
void helper(vector<string>& ans, string s, int start, vector<string>& wordDict, string tmp){
if (start == s.size()){
ans.push_back(tmp.substr(1, tmp.size() - 1));
}
else{
for (int i = start; i < s.size(); i++)
{
string left = s.substr(start, i - start + 1);
if (find(wordDict.begin(), wordDict.end(), left) != wordDict.end()){
helper(ans, s, i + 1, wordDict, tmp + " " + left);
}
}
}
}
vector<string> wordBreak(string s, vector<string>& wordDict) {
vector<string> ans;
if (wordDict.size() == 0) return ans;
helper(ans, s, 0, wordDict, "");
return ans;
}
method 2 dynamic programming
引入dp, 记录字符串s对应的所有句子,因为在回溯过程中,会出现重复的分支,在正常输入下,基本不会出现重复的分支,但比如aaaaaaaaa,[a,aa,aaa,aaaa]这样的,就会导致许多重复分支,所有使用dp记录下来,遇到合适的字符串,直接返回其ans
- 虽然自己用dp尝试过,但思路不对,只是简单想着某一个substr是否出现过,如果出现过,还是对其递归回溯,应该进一步想,如果出现过,直接记录其结果
- 没有找到重复最多的部分进行优化
- 画出解答树优化
unordered_map<string, vector<string>> m;
vector<string> combine(string word, vector<string> prev){
for(int i=0;i<prev.size();++i){
prev[i]+=" "+word;
}
return prev;
}
vector<string> wordBreak(string s, vector<string>& wordDict) {
if (m.count(s)) return m[s]; //take from memory
vector<string> result;
if (find(wordDict.begin(), wordDict.end(), s) != wordDict.end()){ //a whole string is a word
result.push_back(s);
}
for (int i = 1; i<s.size(); ++i){
string word = s.substr(i);
if (find(wordDict.begin(), wordDict.end(), word) != wordDict.end()){
string rem = s.substr(0, i);
vector<string> prev = combine(word, wordBreak(rem, wordDict));
result.insert(result.end(), prev.begin(), prev.end());
}
}
m[s] = result; //memorize
return result;
}
summary
- 递归回溯传入时,传入新的截取过后的字符串,更容易分析
- 要对重复最多的部分进行优化
- 可以选取TLE的case,画出解答树,从而找到重复最多的部分,正确的引入dynamic programming解决问题