(140.单词拆分)
140.单词拆分II
1.题目描述
2.运行结果
3.源码
3.1第一次失败且复杂的尝试
思路:根据wordDict中的字符串去源字符串s头部开始查找,一旦找到,从s的头部删除找到的对应字符串,并用一个临时字符串finded保存已经匹配的字符串,重复前面步骤,如果源字符串s能删除完的话,说明找到了一种划分方法,将finded存入最终要返回的向量res中。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<string> res;
void process(string s,vector<string> wordDict,string finded){
if(s.empty())
res.push_back(finded);
if(!finded.empty())
finded+=" ";
int len=wordDict.size();
string temp;
for(int i=0;i<len;i++){
temp=s;
if(!s.find(wordDict[i],0)){
temp.erase(0,wordDict[i].size());
process(temp,wordDict,finded+wordDict[i]);
}
}
return ;
}
vector<string> wordBreak(string s, vector<string>& wordDict) {
string finded="";
process(s,wordDict,finded);
return res;
}
int main(){
cout<<"请输入字符串:";
string str;
cin>>str;
vector<string> words;
cout<<"请输入单词数目:";
int number;
cin>>number;
cout<<"请输入单词:";
string s;
for(int i=0;i<number;i++){
cin>>s;
words.push_back(s);
}
wordBreak(str,words);
for(int i=0;i<res.size();i++){
cout<<res[i]<<endl;
}
return 0;
}
这个思路比较简单,相当于穷举,在用例较小的情况下可以运行通过,但是对于一些长的用例和特殊的用例就不行了。例如:
时间复杂度应该是阶乘级别的,时间复杂度过大。
3.2第二次成功的尝试
第二次主要是为了解决第一次碰到的未通过的用例。在参考了评论中的一些大佬的思想,加入了“剪枝”的步骤。大致描述如下:
1、假设源字符串s长度为n+1,且可以被分为两部分,一部分为s[0…k]和s[k…n]。
2、假设有m种划分方式可以让我们从s[0]走到s[k],如果没有一种划分方法能使我们从s[k]走到s[n],按照代码1,m种划分方式走到s[k]后每一种都会试一遍从s[k]走到s[n],结果会发现都失败了,浪费了大量的时间。
3、但是如果第一个到达s[k]的发现从s[k]不可能走到s[n],那么留下一个“标记”,通知后面来的人“此路不通”,后面到达s[k]的先检查一下前面留下的“标记”,如果是不通的,直接返回,则节省了大量时间。
代码如下:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<string> res;
vector<string> wDict;
vector<bool> valid;
vector<int> lengths;
bool process(string s,int start,string finded);
vector<string> wordBreak(string s, vector<string>& wordDict) {
string finded="";
wDict=wordDict;
int len=s.size();
vector<bool> initVisit(len,true);
valid=initVisit;
for(int i=0;i<wordDict.size();i++)
lengths.push_back(wordDict[i].size());
process(s,0,finded);
return res;
}
bool process(string s,int start,string finded){
if(start>=s.length()){
res.push_back(finded);
return true;
}
if(valid[start]==false)
return false;
if(!finded.empty())
finded+=" ";
int len=wDict.size();
for(int i=0;i<len;i++){
if(valid[start] && wDict[i]==s.substr(start,lengths[i])){
int reslen=res.size();
process(s,start+wDict[i].size(),finded+wDict[i]);
if(res.size()==reslen)
valid[start+wDict[i].size()]=false;
}
}
return false;
}
int main(){
cout<<"请输入字符串:";
string str;
cin>>str;
vector<string> words;
cout<<"请输入单词数目:";
int number;
cin>>number;
cout<<"请输入单词:";
string s;
for(int i=0;i<number;i++){
cin>>s;
words.push_back(s);
}
wordBreak(str,words);
for(int i=0;i<res.size();i++){
cout<<res[i]<<endl;
}
return 0;
}
标记就是通过向量valid实现的。
附上运行结果
收获:主要懂得在回溯过程中“剪枝”了,通过让前面走过的人告诉后面来的人一些关于后面的路程的信息,避免后来的人的重复走一些不必要的不通的路径达到节省时间的目的。