题目
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字串中增加空格来构建一个句子,使得句子里所有的单词都在词典中。你可以假设字典中无重复的单词。
返回所有这些可能的句子。
例如,给出
s = "catsanddog"
,
dict = ["cat", "cats", "and", "sand", "dog"]
.
答案为 ["cats and dog", "cat sand dog"]
.
UPDATE (2017/1/4):
wordDict 参数已被更改为字符串列表(而不是一组字符串)。请重新加载代码定义以获取最新更改。
题解
这道题最直观的做法就是直接用递归,DFS得出结果。但是直接采用DFS会导致部分case超时。所以比较常用的方法就是DP+DFS结合。其中用DP得出当前字符串s
是否可分,如果不可分直接返回结果。如果可分,再调用DFS得出所有分割的结果。
DP中,flag[i]
表示前i
个字符是否可分,可分即为true
否则为false
。则flag[n]
即为字符串s
是否可分。
技巧:
采用unordered_set
代替原来的wordDict
,方便快速查找。
记录得到wordDict
中最长的字符串的长度maxLen
,则DFS中需要考虑的最大长度即为maxLen
代码
class Solution {
public:
void dfs(string &s,unordered_set<string> &hash,int idx,int maxLen,vector<string> &res,string path,vector<vector<int>> &flag){
int n=s.size();
if(idx>=n){
path.erase(path.begin());
res.push_back(path);
return;
}
for(int i=0;i<maxLen&&i+idx<n;i++){
string tmp="";
if(flag[idx][idx+i]==-1) continue;
else if(flag[idx][idx+i]==0){
tmp=s.substr(idx,i+1);
if(hash.find(tmp)==hash.end()){
flag[idx][idx+i]=-1;
continue;
}
else{
flag[idx][idx+i]=1;
}
}
else{
tmp=s.substr(idx,i+1);
}
dfs(s,hash,idx+i+1,maxLen,res,path+" "+tmp,flag);
}
}
bool isBreak(string &s,unordered_set<string> &hash,int maxLen){
int n=s.size();
vector<bool> flag(n+1,false);
flag[0]=true;
for(int i=1;i<=n;i++){
for(int j=i;j>0&&i-j+1<=maxLen;j--){ //这里加上i-j+1<=maxLen,可以较大的提高运行效率,减少运行时间
string tmp=s.substr(j-1,i-j+1);
if(hash.find(tmp)==hash.end()) continue;
if(flag[j-1]==true) flag[i]=true;
}
}
return flag[n];
}
vector<string> wordBreak(string s, vector<string>& wordDict) {
int n=s.size();
vector<string> res;
string path;
unordered_set<string> hash;
vector<vector<int>> flag(n,vector<int>(n,0));
int maxLen=0;
for(auto &str:wordDict){
maxLen=max(maxLen,(int)str.size());
hash.insert(str);
}
if(isBreak(s,hash,maxLen)==false) return res;
dfs(s,hash,0,maxLen,res,path,flag);
return res;
}
};