方法一:Backtracing+Memoization
遇到在wordlist里的单词就继续深搜,memo[i] 用来保存下标i开始到结束的字符串是否能拆分,如果之前保存过,就直接返回,不用重复计算。
用start记录开始的下标,这样就不用传字符串作为参数了,当然用字符串也可以。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
vector<int> memo(s.size(),-1);
return dfs(s,0,wordSet,memo);
}
bool dfs(string s, int start, unordered_set<string> &wordSet, vector<int> &memo){
if (start==s.size()) return true;
if (memo[start]!=-1) return memo[start];
for (int i=start+1;i<=s.size();++i){
if (wordSet.count(s.substr(start, i-start)) && dfs(s, i, wordSet, memo)) { // [start,i-1] [i,..]
return memo[start] = 1;
}
}
return memo[start] = 0;
}
};
如果不用memorization,时间复杂度为 O(2^n)。
用了memorization,时间复杂度 O(n^2),
空间复杂度 O(n),递归深度最多为n。
关于memorization的时间复杂度:
For solution 2, please check whether my thought process for getting time complexity is correct or not.
If we don't use memorization the recurrence relation of time complexity will be :
T(n) = T(n-1) + T(n-2) + T(n-3) + .... + T(1)
Now if we add memorization, by the time we finish doing T(n-1), We already have the memorization result for n - 2, n - 3 ... 1.
So to check if T(n-2) is valid only takes O(1) time, same goes for the T(n-3) .....T(1)
The recurrence relation now becomes T(n) = T(n-1) + n - 2
Since we are considering big-O notation, we can drop the constant term, it now becomes T(n) = T(n-1) + n.
Using master theorem, we can easily get the time complexity for this time recurrence relation is O(n^2).
https://stackoverflow.com/questions/21273505/memoization-algorithm-time-complexity
方法二:DP
dp[i] 表示前i个字符是否能够被拆分,目标dp[n]
base case: dp[0]=true,可以把wordDict里的元素都置为true
注意本题dp[i]不是表示为下标0~i是有原因的,如果这样表示当然也可以,都需要进行修改,且较为麻烦。因此做dp问题的时候要想一想怎样比较容易写。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> a;
for (string word:wordDict)
a.insert(word);
vector<bool> dp(s.size()+1,false); // dp[i] 前i个元素是否能拆分 0~i-1
dp[0] = true;
for (int i=1;i<=s.size();++i){
for (int j=i-1;j>=0;--j){ // 0~j-1 j~i-1
if (dp[j] && a.count(s.substr(j,i-j))){
dp[i] = true;
break;
}
}
}return dp[s.size()];
}
};
时间复杂度 O(n^2)
空间复杂度 O(n)
方法三:BFS
这道题也可以bfs来做,和jump game类似,能到字符串最后说明可以拆分。
=========================================================
Word Break II
方法一:Backtracing+Memoization
class Solution {
public:
unordered_map<int,vector<string>> memo; // start -> vector<string>
vector<string> wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
return dfs(s,0,wordSet);
}
vector<string> dfs(string s, int start, unordered_set<string> &wordSet){
if (memo.count(start)) return memo[start];
vector<string> res;
if (start==s.size())
res.push_back("");
for (int i=start+1;i<=s.size();++i){
if (wordSet.count(s.substr(start, i-start))) { // [start,i-1] [i,..]
vector<string> tmp=dfs(s, i, wordSet);
for (string str:tmp){
res.push_back(s.substr(start, i-start) + (str==""?"":" ") + str);
}
}
}
memo[start] = res;
return res;
}
};
时间复杂度 O(n^3)
空间复杂度 O(n^3) vector<string> 需要O(n^2)
方法二:DP
思路一样,只不过dp[i]里面保存,能够组成前i个的 用来拼接的word。不过这种方法空间上会超时,没法AC。
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> a;
for (string word:wordDict)
a.insert(word);
vector<vector<string>> dp(s.size()+1);
dp[0].push_back("");
for (int i=1;i<=s.size();++i){
for (int j=i-1;j>=0;--j){
if (dp[j].size()!=0 && a.count(s.substr(j,i-j))){
for (string x:dp[j]){
dp[i].push_back(x + (x==""?"":" ") + s.substr(j,i-j));
}
}
}
}
return dp[s.size()];
}
};
时间复杂度 O(n^3) 两层循环n^2,每次循环把数组元素加到dp[i],O(n)
空间复杂度 O(n^3)
转载于:https://www.cnblogs.com/hankunyan/p/9385356.html