139. 单词拆分
给定一个非空字符串 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
dp[i][j]表示s[i]到s[j]是否存在于worddict中;
递推公式(对i和j中每个k):
if(dp[i][k]&&dp[k+1][j]){
dp[i][j]=1;
整复杂了;
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
for(string w:wordDict)
words[w]=1;
int n=s.size();
dp.resize(n,vector<bool> (n,0));
for(int i=0;i<n;i++)
for(int j=i;j<n;j++)
if(words[s.substr(i,j-i+1)])
dp[i][j]=1;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
for(int k=0;k<j;k++){
if(dp[i][k]&&dp[k+1][j]){
dp[i][j]=1;
break;
}
}
}
return dp[0][n-1];
}
private:
unordered_map<string,bool> words;
vector<vector<bool>> dp;
};
动态规划2
dp[j]表示,s[j]之前是否都在字典中;
递推公式:if(dp[j]&&words[s.substr(j,i-j)]) dp[j]=1;
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
for(string w:wordDict)
words[w]=1;
int n=s.size();
dp.resize(n+1,0);
dp[0]=1;
for(int i=1;i<=n;i++){
if(words[s.substr(0,i)]) dp[i]=1;
for(int j=0;j<n;j++)
{
if(dp[j]&&words[s.substr(j,i-j)]){ //为i-j
dp[i]=1;
break;
}
}
}
return dp[n];
}
private:
unordered_map<string,bool> words;
vector<bool> dp;
};
140. 单词拆分 II
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。
说明:
分隔时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
输入:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
输出:
[
"cats and dog",
"cat sand dog"
]
示例 2:
输入:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
输出:
[
"pine apple pen apple",
"pineapple pen apple",
"pine applepen apple"
]
解释: 注意你可以重复使用字典中的单词。
示例 3:
输入:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
输出:
[]
解法1:回溯
回溯得到每一个解,大量重复字符超时;
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
for(string s:wordDict)
word[s]=1;
tmp="";
dfs(s,0);
return res;
}
private:
string tmp;
vector<string> res;
unordered_map<string,bool> word;
void dfs(string s,int i)
{
if(i==s.size()) {
tmp.erase(tmp.size()-1);
res.push_back(tmp);
tmp+=" ";
return;
}
for(int j=i;j<s.size();j++)
{
if(word[s.substr(i,j-i+1)]){
tmp+=s.substr(i,j-i+1)+" ";
dfs(s,j+1);
tmp.erase(tmp.size()-(j-i+1)-1);
}
}
return;
}
};
回溯+剪枝
从后往前填满map;
剪枝条件:map中保存了剩余字符串的分割方案;
结束条件:剩余字符串为空;
遍历worddict逐个匹配当前字符串的第一个词,匹配成功后进入下一层;
到最后一层匹配最后的一个词,将该词保存进map中;
往前边加“ ”边放入map中,直到回到最初的s,返回res即可;
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
return dfs(s,wordDict);
}
private:
unordered_map<string,vector<string>> map;
vector<string> dfs(string s,vector<string>& wordDict){
if(map.count(s)) return map[s]; //关键点 map.count(s)
if(s.empty()) return {""};
vector<string> res;
for(auto w:wordDict){
if(s.substr(0,w.size())==w){ //匹配成功
vector<string> tmp=dfs(s.substr(w.size()),wordDict);
for(auto t:tmp)
res.push_back(w+(t.empty()?"":" "+t));
}
}
map[s]=res;
return res;
}
};
注意点
查找字典中有无s,用map.count(s)
,并非map[s].count();