给定一个非空字符串 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
想要用动态规划的方法做,先得把列表 wordDict放到一个包中,
unordered_set<string> wordset(wordDict.begin(),wordDict.end());
即把 wordDict放到wordset中,
然后开始确定递推公式,这里我们使用这个思想——把s中的字母循环后,确定到第几个字母,截取的单词符合wordDict,然后把这个字母打上j的标记,然后再从j字母开始,到下一个能截取单词并符合wordDict,这样我们可以直接从后面开始,不需要一遍一遍的遍历了
if (wordSet.find(word) != wordSet.end() && dp[j]) {
dp[i] = true;
然后就可以准备初始化了,首先dp[0]的含义,我们使用的是默认数组是false,后面通过是否为true来判断可否拆分为wordDirt中的单词,所以一开始的dp[0]得定义成true。
于是所有的代码为
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()];
}
};
这个题目用背包问题理解,就是把s理解成背包,wordDict中的一个一个单词理解成物品,物品是可以无限使用的,问题是,我能否用一个个单词把s背包填满,是则返回true,否则返回false。
但值得注意的一点是这个s背包在放物品时必须时按照s中单词的顺序来的,所以我们必须先遍历 背包,再遍历物品。
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
此题便是求排列数。