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.html
Note:數組
Example 1:app
Input: s = "" wordDict = Output: catsanddog["cat", "cats", "and", "sand", "dog"][ "cats and dog", "cat sand dog" ]
Example 2:函數
Input: s = "pineapplepenapple" wordDict = ["apple", "pen", "applepen", "pine", "pineapple"] Output: [ "pine apple pen apple", "pineapple pen apple", "pine applepen apple" ] Explanation: Note that you are allowed to reuse a dictionary word.
Example 3:post
Input: s = "catsandog" wordDict = ["cats", "dog", "sand", "and", "cat"] Output: []
這道題是以前那道Word Break 拆分詞句的拓展,那道題只讓咱們判斷給定的字符串可否被拆分紅字典中的詞,而這道題加大了難度,讓咱們求出全部能夠拆分紅的狀況,就像題目中給的例子所示。以前的版本中字典wordDict的數據類型是HashSet,如今的不知爲什麼改爲了數組vector,並且博主看到第二個例子就笑了,PPAP麼,哈哈~優化
根據老夫行走江湖多年的經驗,像這種返回結果要列舉全部狀況的題,十有八九都是要用遞歸來作的。當咱們一時半會沒有啥思路的時候,先不要考慮代碼如何實現,若是就給你一個s和wordDict,不看Output的內容,你會怎麼找出結果。好比對於例子1,博主可能會先掃一遍wordDict數組,看有沒有單詞能夠當s的開頭,那麼咱們能夠發現cat和cats均可以,好比咱們先選了cat,那麼此時s就變成了 "sanddog",咱們再在數組裏找單詞,發現了sand能夠,最後剩一個dog,也在數組中,因而一個結果就出來了。而後回到開頭選cats的話,那麼此時s就變成了 "anddog",咱們再在數組裏找單詞,發現了and能夠,最後剩一個dog,也在數組中,因而另外一個結果也就出來了。那麼這個查詢的方法很適合用遞歸來實現,由於s改變後,查詢的機制並不變,很適合調用遞歸函數。再者,咱們要明確的是,若是不用記憶數組作減小重複計算的優化,那麼遞歸方法跟brute force沒什麼區別,大機率沒法經過OJ。因此咱們要避免重複計算,如何避免呢,仍是看上面的分析,若是當s變成 "sanddog"的時候,那麼此時咱們知道其能夠拆分紅sand和dog,當某個時候若是咱們又遇到了這個 "sanddog"的時候,咱們難道還須要再調用遞歸算一遍嗎,固然不但願啦,因此咱們要將這個中間結果保存起來,因爲咱們必需要同時保存s和其全部的拆分的字符串,那麼可使用一個HashMap,來創建兩者之間的映射,那麼在遞歸函數中,咱們首先檢測當前s是否已經有映射,有的話直接返回便可,若是s爲空了,咱們如何處理呢,題目中說了給定的s不會爲空,可是咱們遞歸函數處理時s是會變空的,這時候咱們是直接返回空集嗎,這裏有個小trick,咱們其實放一個空字符串返回,爲啥要這麼作呢?咱們觀察題目中的Output,發現單詞之間是有空格,而最後一個單詞後面沒有空格,因此這個空字符串就起到了標記當前單詞是最後一個,那麼咱們就不要再加空格了。接着往下看,咱們遍歷wordDict數組,若是某個單詞是s字符串中的開頭單詞的話,咱們對後面部分調用遞歸函數,將結果保存到rem中,而後遍歷裏面的全部字符串,和當前的單詞拼接起來,這裏就用到了咱們前面說的trick。for循環結束後,記得返回結果res以前創建其和s之間的映射,方便下次使用,參見代碼以下:url
解法一:spa
class Solution { public: vector<string> wordBreak(string s, vector<string>& wordDict) { unordered_map<string, vector<string>> m; return helper(s, wordDict, m); } vector<string> helper(string s, vector<string>& wordDict, unordered_map<string, vector<string>>& m) { if (m.count(s)) return m[s]; if (s.empty()) return {""}; vector<string> res; for (string word : wordDict) { if (s.substr(0, word.size()) != word) continue; vector<string> rem = helper(s.substr(word.size()), wordDict, m); for (string str : rem) { res.push_back(word + (str.empty() ? "" : " ") + str); } } return m[s] = res; } };
咱們也能夠將將主函數自己看成遞歸函數,這樣就不用單獨的使用一個遞歸函數了,不過咱們的HashMap必須是全局了,寫在外部就行了,參見代碼以下:code
解法二:htm
class Solution { public: unordered_map<string, vector<string>> m; vector<string> wordBreak(string s, vector<string>& wordDict) { if (m.count(s)) return m[s]; if (s.empty()) return {""}; vector<string> res; for (string word : wordDict) { if (s.substr(0, word.size()) != word) continue; vector<string> rem = wordBreak(s.substr(word.size()), wordDict); for (string str : rem) { res.push_back(word + (str.empty() ? "" : " ") + str); } } return m[s] = res; } };
相似題目:
參考資料:
https://leetcode.com/problems/word-break-ii/description/
https://leetcode.com/problems/word-break-ii/solution/