您的位置:首页 > 其它

LeetCode_Word Break

2014-07-06 19:55 232 查看
Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

For example, given

s =
"leetcode"
,

dict =
["leet", "code"]
.

Return true because
"leetcode"
can be segmented as
"leet
code"
.

之前好像是写过这个题目的解题报告,说实话这个题目我实在是理解了好久。今天花了一整天时间研究这个题目,最终自己给出了一个O(n^3)的dp算法,然后在Discuss中看了各路大神们的算法,我真是太。。。

话不多说,正题:

思路1:dfs

深度优先搜素,对于字符串s[0,..,n]每次判断其前缀是否可以按照字典所给出的单词进行分词操作,递归进行,分词之字符串尾时返回,代码如下:

//递归求解每次检查当前字串前缀,如果可分则继续深度查询
class Solution {
public:
bool wordBreak(string s, unordered_set<string> &dict) {
issegment=false;
wordBreak(s,0,dict);
return issegment;
}
void wordBreak(const string &s,int pos,unordered_set <string> &dict){
if(pos==s.size()) issegment=true;
if(issegment) return;
for(int i=pos;i<s.size();i++){
auto iter=dict.find(s.substr(pos,i-pos+1));
if(iter!=dict.end()){
wordBreak(s,i+1,dict);
}
}
}
private:
bool issegment;
};


dfs有太多的重复计算,具体图例在我第一遍作的时候就已经画图了,因此想到dp:

思路2:dp

我自己的想法是,对于字符串s[i,..,j]是否可以按照字典所给单词进行分词,存在以下两种情况:

1) s[i,..,j]在字典dict中;

2) 至少存在一个k(i<k<j)使得s[i,..,k]和s[k+1,..,j]都可以被分词;

为此使dp[i][j]表示s[i,..,j]是否可以被分词,递归式如下:

dp[i][j] = true if dictContain(s[i,..,j]) or (dictContain(s[i,..,k])&&dictContain(s[k+1,..,j]))

= false other

由此,有点类似于矩阵连乘问题,共有O(n^2)个子问题,每个子问题有包括O(n)种选择,算法时间复杂度O(n^3),空间复杂度O(n^2)。

代码如下:

#include <string>
#include <vector>
using namespace std;

class Solution{
public:
bool wordBreak(string s,unordered_set <string> &dict){
int len=s.size();
if(!len){
return false;
}

vector <vector <bool> > dp(len,vector <bool>(len,0));

//initialize dp
for(int i=0;i<len;i++){
dp[i][i]=dictContain(s.substr(i,1),dict);
}
//dp
for(int l=2;l<=len;l++){
for(int beg=0;beg+l<=len;beg++){
dp[beg][beg+l-1]=dictContain(s.substr(beg,l),dict);
if(dp[beg][beg+l-1]){continue;}
for(int k=1;k<l;k++){
dp[beg][beg+l-1]=dp[beg][beg+k-1]&&dp[beg+k][beg+l-1];
if(dp[beg][beg+l-1]) break;
}
}
}
return dp[0][len-1];
}
bool dictContain(const string &s,const unordered_set <string> &dict){
auto iter=dict.find(s);
if(auto==dict.end()){
return false;
}
return true;
}
}


思路3:dp+dfs

看了一位大神的代码,总结一下,发现其实换个角度,我们只需要考虑s[0,..,i]是否可以被分词,s[0,..,j]可以被分词的充分必要条件是:

1)存在一个k(0<k<i)使得s[0,..,k]可以被分词并且s.substr(k+1,j)是字典dict中的单词 OR

2)s[0,..,j]本身就在字典dict中;

由此每个子问题的子问题变为只有1个,同时对于子问题的选择有O(n)种,为此使用数组

dp[0,s.size()]记录分词信息,其中dp[i]表示s[0,..,i]是否可以被分词,

dp[i] = true if dictContain(s[0,..,i]) or ((dictContain(s[k+1,..,j]))&&dp[k])

最终结果为dp[s.size()-1],时间复杂度O(n^2),空间O(n),代码如下:

class Solution{
public:
bool wordBreak(string s,unordered_set <string> &dict){
int len=s.size();
vector <bool> dp(len+1,0);
dp[0]=true;
for(int i=1;i<=len;i++){
//check if s.substr(0,i) can be broken
string str=s.substr(0,i);
for(int j=0;j<i;j++){
if(dp[j]&&dictContain(str,dict)){
dp[i]=true;
break;
}
str.erase(str.begin());
}
}
return dp[len];
}
bool dictContain(string s,const unordered_set <string> &dict){
auto iter=dict.find(s);
if(iter!=dict.end()){
return true;
}
return false;
}
} ;


最后说一句忘了在哪个文章中看到的一句话:dp真是一个神奇的东西,只有你自己写了才能体会到,dp代码的简洁美
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: