题目描述
给定一个字符串s和一组单词dict,在s中添加空格将s变成一个句子,使得句子中的每一个单词都是dict中的单词
返回所有可能的结果
例如:给定的字符串s =“catsanddog”,
dict =[“cat”, “cats”, “and”, “sand”, “dog”].
返回的结果为[“cats and dog”, “cat sand dog”].
思路
这道题首先想到的就是递归的方法,一个长的字符串,查询到一小部分在词典以内的,那么剩下的部分继续递归就行了。
递归的优化当然就是想想能不能自底向上来解决。因此用动态规划的方法,才是本题的最优方案。
动态规划的几个步骤还记得吗?
- 1.定义数组含义
- 2.找到递推关系
- 3.找出初始值
其实这道题不算是特别典型的动态规划。
定义数组dp
考虑定义这样的一个数组:dp[i][j]是存储的一个01值,表示的是对于字符串s,从i作为起始,一个往后j个字符,这样的一个字符串,是否在字典里。那么可以对字符串进行两层遍历,则将字典转化成了这样一个二维数组,这一步是没有问题的。为什么要搞这样的一个数组,当然是为了方便查询了。
如何查询?
有了dp数组,然后对这个s字符串,如何处理呢
最重要的当然就是如何一个不剩地将这些组合找出来,而“catsanddog”这10个字符,可以形成的分割形态,是指数级别的,其实我们不用都把他们列举出来,比如查到“ca”不在字典里,那么后面的“tsanddog”不论能组成多么天花乱坠的词,都不能行,这就去除了许多冗余的计算。因此,我们就要考虑这个遍历的顺序,究竟是怎么样的!
现在可以肯定的是:字符串,以第一个字符开头的所有词(或者以最后一个字符为结尾的所有词)因为它们都是在边上,所以这些词都要遍历一次才行。接下来提供两种遍历的方法:
- 前向遍历
“catsanddog”
即以c,ca,cat,cats… 的顺序来遍历,考虑如下:当遍历到cat的时候,已经形成了一个单词,那么后面该进行遍历的应该是字串“sanddog”了,那么就递归进去遍历,然后这个子串遍历完了之后,跳出来应该是接着上面的cat,继续查询cats,查到cats,递归进入"anddog",出来之后继续catsa,catsan,catsand…。 - 后向遍历
以catsanddog,atsanddog,tsanddog,sanddog…的顺序来遍历。道理和前向遍历类似。
如果上面您没看懂,下面这个遍历顺序您应该很容易看懂了
那么有了这个步骤,后面的代码也就好写了。还有一个问题,就是在每次输出合法答案的时候,怎么实现?这里是用全局变量mystring+递归实现,判定合法的条件之后,把答案添加到result中,但是跳出递归的时候,要记得把mystring中的单词弹出来!这个倒是比较巧妙。
废话不多,看代码:
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <list>
#include <set>
#include <unordered_set>
#include <map>
#include <queue>
#include <algorithm>
#include <numeric> //accmulate
#include <functional> //greater<int>()!!!
using namespace std;
class Solution {
public:
vector<string> wordBreak(string s, unordered_set<string> &dict) {
dp = new vector<bool>[s.size()];
for (int i=0;i<s.size();i++){
for (int j=i;j<s.size();j++){
dp[i].push_back(match(s.substr(i,j-i+1), dict));
}
}
output(s.size()-1, s);
return result;
}
bool match(string s, unordered_set<string> &dict){
if (dict.find(s)!=dict.end()) return true;
else return false;
}
void output(int i, string s){
if(i==-1){
string single ;
for(int j=mystring.size()-1;j>=0;j--){
single += mystring[j];
if (j!=0) single += " ";
}
result.push_back(single);
}
else{
for(int k=i;k>=0;k--){
if (dp[k][i-k]){
mystring.push_back(s.substr(k,i-k+1)); //存入合法单词
output(k-1,s);
mystring.pop_back(); //弹出相应的单词
}
}
}
}
vector<string> result;
vector<string> mystring;
vector<bool> *dp;
};
不懂的留言哦,可能我没写清楚,只有我自己看得懂哈哈。