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解決問題