题目描述:
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"
.
Trail1:
首先想到的是贪心的算法,可是直接就能举出反例:
比如:S = “aaaaa”
dict = [ ''aaaa" , " aaa" , "aa"]
显然,贪心是不对的,因此还没开始编代码就已经被否决了。
Trail 2:
对于字符串S,枚举取出S的 i-length 前缀,如果是字典中的词的话,那么对剩下的 S.size() - i 个字符递归调用该函数即可,代码如下:
/*if(s.empty())return false;
bool label = false;
bool *labels = new bool[s.size()];
// labels[0] = false;
for(int i = 1; i < s.size() ; ++i)
{
string temp = s.substr(0,i);
if(dict.find(temp) != dict.end())
{
string temp = s.substr(i);
label = wordBreak(temp,dict);
}
if(label)break;
}
return label;
比如对于S的任何一个 i-length 前缀 和 j-length 前缀,不是一般性,假设 i < j , 那么在判断 j-length 前缀的时候是存在重复计算 i-length 前缀的,然后 i-length前缀在前面已经计算过了,但是却没有保存下来。因此需要使用数组保存已经计算过的问题。因此DP就是很自然的事儿了。
Trail 3:
定义labels[ i ] 表示数组的前 i-length前缀是否可以拆成字典中的词。那么很显然的有:
labels[ i ] = 1 如果存在 labels[ j ] && ( S[ j +1, i ] 是否在dict中) ( 0 < j <= i )
否则就为0.
因此代码如下:
if(s.empty())return false;
bool label = false;
bool *labels = new bool[ s.size() + 1 ];
for(int i = 0 ; i < s.size() + 1; ++i)
labels[i] = false;
labels[0] = true;
for(int i = 1 ; i <= s.size(); ++i)
{
for(int j = 0; j < i ; ++j)
{
//bool label1 = labels[j];
string s2 = s.substr(j,i-j);
//bool label2 = false;
//if(s2.empty() || (!s2.empty() && dict.find(s2) != dict.end()))
if(dict.find(s2) != dict.end() && labels[j])
//label2 = true;
//if(label1 && label2)
labels[i] = true;
}
}
return labels[s.size()];
因此,问题得解。
总结:多谢另一半的提醒,重复计算这个问题确实是很严重,我在计算机上测试过,相差的时间挺大。
另外,DP一直是我的一个弱项,推荐给大家之前我转载过的一篇文章,背包问题的,这个是DP的一个超级经典的应用。
http://blog.youkuaiyun.com/xuqingict/article/details/18766029
非常不错的文章,百读不厌!!!