给你一个字符串 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
提示:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s
和wordDict[i]
仅由小写英文字母组成wordDict
中的所有字符串 互不相同
回溯 + 记忆化搜索/备忘录
class Solution {
public:
bool backtracking(string& s,unordered_set<string>& wordset,vector<bool>& memo,int startindex){
if(startindex >= s.size()){
return true;
}
if(!memo[startindex]) return memo[startindex]; // 如果memo[startindex]此处的状态已经修改为0了,说明,前面记录过
for(int i = startindex;i < s.size();i++){
string str = s.substr(startindex,i-startindex+1);
if(wordset.find(str) != wordset.end() && backtracking(s,wordset,memo,i+1)){
return true;
}
}
memo[startindex] = false; //程序能走到这里,说明容量startindex是不可分的
return false;
}
bool wordBreak(string s, vector<string>& wordDict) {
//回溯 加 备忘录(记忆化搜索)
unordered_set<string>wordset(wordDict.begin(),wordDict.end());
vector<bool>memo(s.size(),1); // 1表示初始状态
return backtracking(s,wordset,memo,0);
}
};
DP
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
//利用worddict的物品,去凑s
//dp[i]:凑满容量为i的背包,dp[i] = true;
vector<bool>dp(s.size() + 1,false);//先初始化为false,因为不知道状态
//递推关系:如果j之前的都是true,那么j-i之间也为true的话。那就是true
//初始化
dp[0] = true; // 不需要凑,默认为true;
//遍历顺序:完全背包,正序; 必须要按一定顺序才能凑,所以先背包,后物品
//定义一个 set,判断该字串是否在s中
unordered_set<string>wordset(wordDict.begin(),wordDict.end());
//i表示长度
for(int i = 1;i <= s.size();i++){ // 背包
for(int j = 0;j < i;j++){ // 物品 // 选的物品都是要在i和j之间选
string str = s.substr(j,i-j);
if(dp[j] == true && wordset.find(str) != wordset.end()){
dp[i] = true;
}
}
}
return dp[s.size()];
}
};