难度:中等
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
- 拆分时可以重复使用字典中的单词。
- 你可以假设字典中没有重复的单词。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"] 输出: true 解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"] 输出: true 解释: 返回 true 因为"
applepenapple"
可以被拆分成"
apple pen apple"
。 注意你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 输出: false
题目分析:
考虑使用回溯法,先将单词使用哈希set进行存储,方便搜索。再将一个个子串切割进行对比,直到子串为空。
结束条件:当字符串为空时,返回true
all station:使用for循环,不需要使用全局变量index,因为传入的是子字符串作为下次递归的字符串,已经在之前的基础上进行了操作,所以不需要index,当然也可以用,这样的话传入字符串一直是s,而不是子字符串。
优化:因为暴力回溯会有很多重复计算,在leetcode会超时,所以使用记忆化所有,遍历过的字符串就不需要再遍历了
参考代码:(暴力回溯,超时)
//暴力回溯,超时
class Solution{
public:
bool wordBreak(string s, vector<string>& wordDict)
{
//初始化str_set
unordered_set<string> str_set(wordDict.begin(), wordDict.end());
return back_search(s, str_set);
}
bool back_search(string s, unordered_set<string>& str_set)
{
//结束条件
if (s.empty())
{
return true;
}
for (int i = 1; i <= s.size(); i++)
{
if (str_set.find(s.substr(0, i)) != str_set.end())
{
if (back_search(s.substr(i), str_set))
return true;
}
}
return false;
}
};
参考代码:(记忆化搜索)
//回溯,记忆化搜索
class Solution {
public:
unordered_map<string, bool>map;//用于记忆化搜索,记忆是否遍历过
bool wordBreak(string s, vector<string>& wordDict)
{
unordered_set<string> m(wordDict.begin(), wordDict.end());
return dfs(s , m);
}
bool dfs(string s, unordered_set<string>& m)
{
//结束条件
if (s.empty())
{
return true;
}
//当剩余字符串搜索过不满足条件时,那么直接返回false即可
if (map[s])
return false;
for (int i = 1; i <= s.size(); i++)//从1开始,很关键
{
//substr的第一个参数是start,第二个参数是length
string temp = s.substr(0, i);//字符串是0~i-1
if (m.find(temp) != m.end())//如果存在于字典中就继续操作
{
//将当前遍历到的子字符串切掉之后剩余部分给下次递归
//如果不满足条件,那么将剩余字符串做个标志,下次就不需要再重复遍历了
if (!dfs(s.substr(i), m))//从第i个字符开始到最后一个
map[s.substr(i)] = true;
else
return true;
//if (dfs(s.substr(i), m))
// return true;
}
}
return false;
}
};