标签:dynamic programming backtracking leetcode
题目:
Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.
Return all such possible sentences.
For example, given
s = "catsanddog"
,
dict = ["cat", "cats", "and", "sand", "dog"]
.
A solution is ["cats and dog", "cat sand dog"]
.
Answer 1: BF递归解 TLE
思路:每次维护一个当前结果集,然后遍历剩下的所有子串,如果子串在字典内出现,则保存一下结果。并放入下一层递归剩下的所有字符。
不过这种做法在Leetcode中会超时。
Error Code:
class Solution { public: vector<string> wordBreak(string s, unordered_set<string> &dict) { vector<string> ret; if(s.size() == 0) return ret; wordBreak_helper(s, dict, 0, "", ret); return ret; } private: void wordBreak_helper(string s, unordered_set<string>& dict, int pos, string tmp, vector<string>& ret) { if(pos == s.size()) { ret.push_back(tmp.substr(0, tmp.size()-1)); //去除空格 return; } for(int i = pos; i < s.size(); i++) { for(int j = 1; j < s.size()-i; j++) { string substr = s.substr(pos, j); if(dict.find(substr) != dict.end()) { wordBreak_helper(s, dict, i+1, tmp + " " + substr, ret); } } } } };
思路:这道题,我们借鉴Word Break中的动态规划思路。不过这次我们需要记录每一个拆分结果,所以使用二维数组保存动态规划结果,二维数组形式vector<list<int>> dp, dp[i]表示字符串从坐标 i 开始的所有合法单词的终止位置列表。我们从字符串末尾开始,自底向上去求解动态规划数组,我们以当前stop为终止位置,循环所有合法的开始位置,如果子串在字典内,相应的在dp数组内添加元素。需要注意的是,如果从当前stop开始并无匹配的字典内单词(说明此处不可断),说明可以跳过这个stop做截止位置.
求解完所有可能的组合后,我们通过回溯法,去求解所有可能的分割情况。这次自顶向下求,从字符串头开始,遍历所有结果,直到到达字符串末尾。
Attention:
1. stop的含义,表示单词结束坐标的后一位,即下一个单词的首位或字符串length。所以stop从s.size()开始。
for(int stop = s.size(); stop >= 0; stop--)2. 如果从当前stop开始并无任何字典内单词,可以跳过这个断点。
//如果从stop开始并无匹配的单词,可以跳过审查这个stop坐标的截止单词(stop表示单词的最后一个字母的后一位) if(stop < s.size() && mark[stop].empty()) continue;3. 从当前stop前一位开始,循环所有合法的开始位置,并更新动态规划数组。
/检查以当前stop截止,之前的所有在词典内的单词,并添加进mark中。 for(int start = stop - 1; start >= 0; start--) { string substr = s.substr(start, stop - start); if(dict.find(substr) != dict.end()) { mark[start].push_back(stop); } }4. C++11的for的新用法。
for述句将允许简单的范围迭代:
int my_array[5] = {1, 2, 3, 4, 5}; for (int &x : my_array) { x *= 2; }
for述句的第一部份定义被用来做范围迭代的参数,就像被声明在一般for循环的参数一样,其作用域仅只于循环的范围。而在":"之后的第二区块,代表将被迭代的范围。 并且,循环变量是一个引用,可以在循环中改变它的值。
for(int& stop : mark[index])
程序中,这一句,就表示循环所有的以index开始的单词的stop位置。
5.回溯时,我们注意要维护当前的path,不能用添加子串的新串去替代path,因为这是个循环语句,我们下一个stop依然要使用原本的path(未添加子串).
for(int& stop : mark[index]) { string substr = s.substr(index, stop - index); string newpath = path + (index == 0 ? substr : " " + substr);AC Code:
class Solution { public: vector<string> wordBreak(string s, unordered_set<string> &dict) { //mark[i]存储了从i坐标开始,在字典内的单词的所有截止坐标。 vector<list<int>> mark(s.size(), list<int>()); //从字符串末尾至头 for(int stop = s.size(); stop >= 0; stop--) { //如果从stop开始并无匹配的单词,可以跳过审查这个stop坐标的截止单词(stop表示单词的最后一个字母的后一位) if(stop < s.size() && mark[stop].empty()) continue; //检查以当前stop截止,之前的所有在词典内的单词,并添加进mark中。 for(int start = stop - 1; start >= 0; start--) { string substr = s.substr(start, stop - start); if(dict.find(substr) != dict.end()) { mark[start].push_back(stop); } } } vector<string> ret; generate(mark, 0, s, "", ret); return ret; } private: void generate(vector<list<int>> mark, int index, const string& s, string path, vector<string>& ret) { for(int& stop : mark[index]) { string substr = s.substr(index, stop - index); string newpath = path + (index == 0 ? substr : " " + substr); if(stop == s.size()) ret.push_back(newpath); else generate(mark, stop, s, newpath, ret); } } };
[C++]LeetCode: 113 Word Break II (DP && Backtacking) 求解拆分组合
标签:dynamic programming backtracking leetcode
原文地址:http://blog.csdn.net/cinderella_niu/article/details/42922329