方法一:循环递推的方式
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> dict(wordDict.cbegin(), wordDict.cend());
int n = s.length();
vector<int> f(n+1, 0); //使用了padding技巧,索引0-n
f[0] = 1;
s = " " + s; //漏掉了这一部分,导致后面取s.substr时总是多扣了一位字符,因此结果不对
for(int i=1; i<=n; ++i) {
for(int j=0; j<=i; ++j) {
if(f[j]==1) {
string new_str = s.substr(j+1, i-(j+1)+1); //取可分割点之后的部分
if(dict.count(new_str)) {
f[i] = 1; //dict中可以找得到后面这一段,就说明s前i个字符可以完全 word break
break;
}
}
}
}
return f[n];
}
};
方法二:记忆化的递归
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> dict(wordDict.cbegin(), wordDict.cend());
return wordBreak(s, dict);
}
private:
//vector<int> f(n, -1);
unordered_map<string, bool> mem_;
bool wordBreak(string& s, unordered_set<string>& dict) {
//if(mem_[s]) return ture; //true or false都应该返回
//递归的终止条件:已经有结果了/在dict中存在
if(mem_.count(s)) return mem_[s];
if(dict.count(s)) return mem_[s] = true;
//if(s == " ") return true;
for(int i=1; i<s.length(); i++) {
string left = s.substr(0, i); //从index=0开始,长为i的子串
string right = s.substr(i); //从index=i开始的子串
if(dict.count(right) && wordBreak(left, dict)) { //利用&&的短路效应,递归在后,可以节省时间
return mem_[s] = true; //bug:没有把结果更新到‘记忆’中
}
}
return mem_[s] = false; //bug:没有把结果更新到‘记忆’中
}
};

本文介绍两种实现字符串拆分(word break)的有效算法:循环递推和记忆化递归。通过使用unordered_set快速查找字典中的单词,算法判断输入字符串是否能被字典中的单词完全覆盖。循环递推算法采用动态规划,而记忆化递归则避免重复计算,提高效率。
557

被折叠的 条评论
为什么被折叠?



