题目描述:
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.csdn.net/xuqingict/article/details/18766029
非常不错的文章,百读不厌!!!