## Leetcode 139 单词拆分
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
输入: s = "leetcode", wordDict = ["leet", "code"] 输出: true 解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
题解:
1) 1.DFS+记忆化剪枝 方法 每次将单词拆成两部分,i,k,j 。
i是起始位置,j是结束位置,k是中间位置 ,k从i遍历到j ,
看子字符串s[i,k]和s[k+1,j]是否是目标单词,
是的话直接回溯,不是的话递归下去,如果为1个字母还不是,返回false 。
但是这样会超时,因为包含了大量重复计算的例子; 可以用set对子串失败的情况进行记录;
如果substr在set里,就没有必要继续遍历了;
#include<iostream>
#include<string.h>
#include<set>
using namespace std;
class Solution {
public:
set<string> mem;
bool dfs(string s,int indexl,int indexr,vector<string>& wordDict,int n){
if(indexl>indexr)
return false;
string ss =s.substr(indexl,indexr-indexl+1);
for(int i=0;i<n;i++){
if(ss==wordDict[i]){
return true;
}
}
for(int k=indexl;k<indexr;k++){
string tmpl = s.substr(indexl,k-indexl+1);
string tmpr = s.substr(k+1,indexr-k);
if(mem.find(tmpl)==mem.end()&&mem.find(tmpr)==mem.end()){
if(dfs(s,indexl,k,wordDict,n)&&dfs(s,k+1,indexr,wordDict,n)){
return true;
}
}
}
mem.insert(ss);//ss是不满足的
return false;
}
bool wordBreak(string s, vector<string>& wordDict) {
int numW = wordDict.size();
return dfs(s,0,s.length()-1,wordDict,numW);
}
};
2) 完全背包
dp[i]表示前i个字母能否被拆成字典里的单词。
如果可以,只要i到j 也在字典里,dp[j]=1。
假设字符串长度为n,wordDict的长度为k。
直接对wordDict遍历查找的时间复杂度是O(n^2k);
如果把wordDict存到set里,由于set的查找时间复杂度是O(log k),
时间复杂度 就优化到了O(n^2 logk)。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int numW = wordDict.size();
int len =s.length();
int dp[len+1];
set<string> Dic;
for(int k=0;k<numW;k++){
Dic.insert(wordDict[k]);
}
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=1;i<=len;i++)
for(int j=i;j<=len;j++){
if(dp[i-1]==1){
string tmp = s.substr(i-1,j-i+1);
if(Dic.find(tmp)!=Dic.end())
dp[j]=1;
}
}
return dp[len];
}
};