139.单词拆分
1.回溯法。普通的回溯这一题会超时,这一题如果用回溯法还要加上记忆化搜素。因为我们在回溯的过程中做了大量的重复工作,所以我们可以把之前的结果进行标记。再遇到相同的情况,我们直接把之前的结果拿来用。
class Solution {
private:
bool backtracking(const string& s, const unordered_set<string>& wordSet,
vector<bool>& memory, int startIndex) {
if (startIndex >= s.size()) {
return true;
}
// 如果memory[startIndex]不是初始值了,直接使用memory[startIndex]的结果
if (!memory[startIndex])
return memory[startIndex];
for (int i = startIndex; i < s.size(); i++) {
string word = s.substr(startIndex, i - startIndex + 1);
if (wordSet.find(word) != wordSet.end() &&
backtracking(s, wordSet, memory, i + 1)) {
return true;
}
}
memory[startIndex] =
false; // 记录以startIndex开始的子串是不可以被拆分的
return false;
}
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
vector<bool> memory(s.size(), 1); // -1 表示初始化状态
return backtracking(s, wordSet, memory, 0);
}
};
2.动态规划。dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词。动规的代码不长,但其实有很多细节需要注意,比如先把字符串数组转成set方便查找截取字符串是否在字典中出现过。还有就是本题一定是先背包再物品,还记得之前说过先背包再物品求的是排列。为什么本题单词拆分必须是排列呢?注意到题目中说字典中的每个单词可以重复使用,本题单词就相当于物品。先背包再物品求排列这个过程中对于不同容量的背包都会把所有的单词都遍历了一遍,就相当于重复使用了单词。而如果先物品再背包,我们会发现对于每个单词我们只使用了一次,之后不再进行重复遍历从而达不到重复使用单词最终结果错误。(详情见随想录本题讲解的拓展部分)(注:dp数组设置成bool数组形式在动规题目中是一种常用方法)
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
vector<bool> dp(s.size() + 1, false);
dp[0] = true;
for (int i = 1; i <= s.size(); i++) { //遍历背包
for (int j = 0; j < i; j++) { //遍历物品
string word =
s.substr(j, i - j); //截取字符串substr(起始位置,截取个数)
if (wordSet.find(word) != wordSet.end() && dp[j]) { //递推公式
dp[i] = true;
}
}
}
return dp[s.size()];
}
};
今日总结:单词拆分。