和LeetCode第 140 题:单词拆分 II(C++)_zj-CSDN博客相似,但是做法不太一样。
本题我用的是字典树+dfs:
class Trie{
public:
struct TrieNode{
bool isEnd = false;
TrieNode* next[26] = {NULL};
};
Trie() : root(new TrieNode) {}
void insert(string s){
auto p = root;
for(int i = 0; i < s.size(); ++i){
int idx = s[i] - 'a';
if(p->next[idx] == NULL) p->next[idx] = new TrieNode;
p = p->next[idx];
}
p->isEnd = true;
}
//s:待查询单词,idx:查询起点,cnt:计数
bool search(string s, int idx, int cnt){//类似于dfs查询
auto p = root;
for(int i = idx; i < s.size(); ++i){
int idx = s[i] - 'a';
if(p->next[idx] == NULL) return false;
p = p->next[idx];
if(p->isEnd){//到达某个单词末尾
//能够走到最后
if(i == s.size()-1) return cnt >= 1;//因为要求至少两个单词组成
//这儿不能有else返回false,因为还要循环检查其他的
//能够查询成功返回true,查询失败就进入下一轮循环
if(search(s, i+1, cnt+1)) return true;
}
}
return false;//全部检查完就会走到这一步,返回false
}
private:
TrieNode *root;
};
class Solution {
public:
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
for(const auto &w : words) tree.insert(w);
vector<string> res;
for(const auto &w : words){//遍历单词
if(tree.search(w, 0, 0)) res.push_back(w);
}
return res;
}
private:
Trie tree;
};
重点是search函数,一开始总想着像140题那样遍历单词的长度来进行普通的search,但是行不通。直接将dfs查询放到search函数里面就可以了。
也可以不使用字典树,使用哈希表:
class Solution {
public:
unordered_set<string> s;
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
s = unordered_set<string>(words.begin(), words.end());
vector<string> res;
for(auto &w : words){//遍历单词
if(dfs(w, 0, 0)) res.push_back(w);
}
return res;
}
bool dfs(string &word, int start, int cnt){
if(start == word.size()) return cnt >= 2;
for(int i = start; i < word.size(); ++i){//遍历每一个可能的长度
string t = word.substr(start, i-start+1);
if(s.count(t) && dfs(word, i+1, cnt+1)) return true;
}
return false;
}
};
可以发现这个方法其实相当的暴力(但是效率还是要比字典树要好),对每一个可能的长度都进行了回溯(dfs),截止条件就是其实位置start == word.size(),此时判断计数器cnt是否大于2就可以了。
那我们可以借助140/139题的思想进行优化,不用每个长度都遍历,只遍历可能的长度:
class Solution {
public:
unordered_set<string> s;
unordered_set<int> lenset;//记录长度
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
s = unordered_set<string>(words.begin(), words.end());
for(const auto &w : words) lenset.insert(w.size());
vector<string> res;
for(auto &w : words){//遍历单词
if(dfs(w, 0, 0)) res.push_back(w);
}
return res;
}
bool dfs(string &word, int start, int cnt){
if(start == word.size()) return cnt >= 2;
for(int i = start; i < word.size(); ++i){//遍历每一个可能的长度
if(lenset.count(i-start+1) == 0) continue;//不存在这个长度
string t = word.substr(start, i-start+1);
if(s.count(t) && dfs(word, i+1, cnt+1)) return true;
}
return false;
}
};
不过效率并没有提升多少。。。