一条easy的题目,但是感觉不是那么容易做的。
第一思路是使用set,去重+排序,然后一个一个遍历,但是的话感觉总是不好做。
然后才使用了其他方法。
方法一:图+bfs
类似这种每个单词之间隔着一个字母就相当于有一条边连着的思想之前遇到过。这里也是类似的思路,但是的话时间消耗有点长。
class Solution {
public:
string longestWord(vector<string>& words) {
unordered_set<string> dict(words.begin(), words.end());
string retStr = "";
queue<string> q;
q.push("");
while (!q.empty()) {
int sz = q.size();
while (sz-- > 0) {
string word = q.front();
if (word.size() > retStr.size())
retStr = word;
q.pop();
for (char c = 'a'; c <= 'z'; ++c)
if (dict.find(word+c) != dict.end())
q.push(word+c);
}
}
return retStr;
}
};
//图,bfs
方法二:Trie
其实自己一开始就该想到的,但是还是看了题目tag才想到的。
类似这种单词前缀的问题,第一个就应该想到Trie。
struct Node {
Node():existed(false), nextNodes(26, nullptr){}
bool existed;
vector<Node*> nextNodes;
};
class Trie {
public:
Trie(): root(new Node()) {}
bool insert(const string& word) {//只有它的所有前缀单词都存在的时候,才插入成功
insertHelper(word, 0, root);
return find(word);
}
bool find(const string& word) {
return findHelper(word, 0, root);
}
private:
Node* root;
Node* insertHelper(const string& word, int index, Node* node) {
if (index == word.size()) {
node = new Node();
node->existed = true;
return node;
}
if (node == nullptr) //如果路径上有一个节点不存在,说明有一个前缀没有,说明不能插入
return nullptr;
(node->nextNodes)[word[index] - 'a'] = insertHelper(word, index+1, (node->nextNodes)[word[index] - 'a']);
return node;
}
bool findHelper(const string& word, int index, Node* node) {
if (index == word.size())
return true;
if (!node)
return false;
return findHelper(word, index+1, (node->nextNodes)[word[index] - 'a']);
}
};
class Solution {
public:
string longestWord(vector<string>& words) {
set<string> dict(words.begin(), words.end());
string retStr = "";
Trie trie;
for (auto& word : dict)//set里面的string都是const的,所以如果要传给一个函数,要保证是const参数的
if (trie.insert(word) && word.size() > retStr.size())
retStr = word;
return retStr;
}
};
//1.单词,相同前缀。。。Trie
//2.只有26个字母,也暗示了这一点
这里的Trie的实现做了一些改动,比如:插入一个单词的时候,只有它的所有前缀单词都存在的时候,才插入成功。
另外还要注意一点:set里面的string都是const的,所以如果要传给一个函数,要保证是const参数的。
之所以平时这一点没有影响到我,是因为平时将元素从set里面拿出来后,没想过要改变它,或者传给一个函数。但是需要保证一个函数不会改变它,才可以传递成功。
方法三:
这个方法其实还是排序方法后面的延续。只不过后面怎么做我没有想起来,这里评论区有人用简洁的方法做出来了。
class Solution {
public:
string longestWord(vector<string>& words) {
sort(words.begin(), words.end());
unordered_set<string> built;
built.insert(string(""));
string retStr = "";
for (string& word : words) {
if (built.find(word.substr(0, word.size()-1)) != built.end()) {
if (word.size() > retStr.size())
retStr = word;
built.insert(word);
}
}
return retStr;
}
};
其实就是说:先将数组排序,每到一个单词,如果它是符合条件的,那么它的那些前缀必然在它前面。我们将它这些前缀都放到一个set里面。每次迭代到一个单词时,查一查比它小一个的前缀单词在不在set里面,如果在,说明所有的小的前缀单词都在,那么是符合要求的单词,插入到set里面;如果不在,说明不符合要求,所以不能插入。
class Solution {
public:
string longestWord(vector<string>& words) {
sort(words.begin(), words.end());
unordered_set<string> built;
built.insert(string(""));
string retStr = "";
for (string& word : words) {
if (built.find(word.substr(0, word.size()-1)) != built.end()) {
if (word.size() > retStr.size())
retStr = word;
built.insert(word);
}
}
return retStr;
}
};
这里的思路还是挺独特的,自己想不出来。
这里与我自己的思路来讲最大的差别就是:这里把符合要求的都放到一个set里面存储起来了。然后新的要查询的时候很方便。