題目描述
給定一個字符串s和一組單詞dict,在s中添加空格將s變成一個句子,使得句子中的每一個單詞都是dict中的單詞
返回所有可能的結果
例如:給定的字符串s =“catsanddog”,
dict =[“cat”, “cats”, “and”, “sand”, “dog”].
返回的結果爲[“cats and dog”, “cat sand dog”].
思路
這道題首先想到的就是遞歸的方法,一個長的字符串,查詢到一小部分在詞典以內的,那麼剩下的部分繼續遞歸就行了。
遞歸的優化當然就是想想能不能自底向上來解決。因此用動態規劃的方法,纔是本題的最優方案。
動態規劃的幾個步驟還記得嗎?
- 1.定義數組含義
- 2.找到遞推關係
- 3.找出初始值
其實這道題不算是特別典型的動態規劃。
定義數組dp
考慮定義這樣的一個數組:dp[i][j]是存儲的一個01值,表示的是對於字符串s,從i作爲起始,一個往後j個字符,這樣的一個字符串,是否在字典裏。那麼可以對字符串進行兩層遍歷,則將字典轉化成了這樣一個二維數組,這一步是沒有問題的。爲什麼要搞這樣的一個數組,當然是爲了方便查詢了。
如何查詢?
有了dp數組,然後對這個s字符串,如何處理呢
最重要的當然就是如何一個不剩地將這些組合找出來,而“catsanddog”這10個字符,可以形成的分割形態,是指數級別的,其實我們不用都把他們列舉出來,比如查到“ca”不在字典裏,那麼後面的“tsanddog”不論能組成多麼天花亂墜的詞,都不能行,這就去除了許多冗餘的計算。因此,我們就要考慮這個遍歷的順序,究竟是怎麼樣的!
現在可以肯定的是:字符串,以第一個字符開頭的所有詞(或者以最後一個字符爲結尾的所有詞)因爲它們都是在邊上,所以這些詞都要遍歷一次纔行。接下來提供兩種遍歷的方法:
- 前向遍歷
“catsanddog”
即以c,ca,cat,cats… 的順序來遍歷,考慮如下:當遍歷到cat的時候,已經形成了一個單詞,那麼後面該進行遍歷的應該是字串“sanddog”了,那麼就遞歸進去遍歷,然後這個子串遍歷完了之後,跳出來應該是接着上面的cat,繼續查詢cats,查到cats,遞歸進入"anddog",出來之後繼續catsa,catsan,catsand…。 - 後向遍歷
以catsanddog,atsanddog,tsanddog,sanddog…的順序來遍歷。道理和前向遍歷類似。
如果上面您沒看懂,下面這個遍歷順序您應該很容易看懂了
那麼有了這個步驟,後面的代碼也就好寫了。還有一個問題,就是在每次輸出合法答案的時候,怎麼實現?這裏是用全局變量mystring+遞歸實現,判定合法的條件之後,把答案添加到result中,但是跳出遞歸的時候,要記得把mystring中的單詞彈出來!這個倒是比較巧妙。
廢話不多,看代碼:
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <list>
#include <set>
#include <unordered_set>
#include <map>
#include <queue>
#include <algorithm>
#include <numeric> //accmulate
#include <functional> //greater<int>()!!!
using namespace std;
class Solution {
public:
vector<string> wordBreak(string s, unordered_set<string> &dict) {
dp = new vector<bool>[s.size()];
for (int i=0;i<s.size();i++){
for (int j=i;j<s.size();j++){
dp[i].push_back(match(s.substr(i,j-i+1), dict));
}
}
output(s.size()-1, s);
return result;
}
bool match(string s, unordered_set<string> &dict){
if (dict.find(s)!=dict.end()) return true;
else return false;
}
void output(int i, string s){
if(i==-1){
string single ;
for(int j=mystring.size()-1;j>=0;j--){
single += mystring[j];
if (j!=0) single += " ";
}
result.push_back(single);
}
else{
for(int k=i;k>=0;k--){
if (dp[k][i-k]){
mystring.push_back(s.substr(k,i-k+1)); //存入合法單詞
output(k-1,s);
mystring.pop_back(); //彈出相應的單詞
}
}
}
}
vector<string> result;
vector<string> mystring;
vector<bool> *dp;
};
不懂的留言哦,可能我沒寫清楚,只有我自己看得懂哈哈。