地址:https://leetcode.com/problems/longest-word-in-dictionary/
题目:
Given a list of strings words
representing an English Dictionary, find the longest word in words
that can be built one character at a time by other words in words
. If there is more than one possible answer, return the longest word with the smallest lexicographical order.
Example 1:
Input: words = [“w”,“wo”,“wor”,“worl”, “world”]
Output: “world”
Explanation: The word “world” can be built one character at a time by “w”, “wo”, “wor”, and “worl”.
Example 2:
Input: words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]
Output: “apple”
Explanation: Both “apply” and “apple” can be built from other words in the dictionary. However, “apple” is lexicographically smaller than “apply”.
Note:
All the strings in the input will only contain lowercase letters.
- The length of words will be in the range
[1, 1000]
. - The length of words[i] will be in the range
[1, 30]
.
理解:
承认愚蠢的我并没有看懂题目。。遂google之
是这样的,找到words
中最长的那个单词,他可以通过其他的words每次加一个字母得到。比如Example 1中,world可以依次由"w",“wo”,“wor”,"worl"添加’o’,‘r’,‘l’,'d’得到。如果有多个可能的答案,返回按字典序最小的,也就是最后一个字母最小的,比如apple和apply,字典序apple在前,返回apple。
实现1:
看到discussion里的Trie+DFS,感觉略麻烦,要去吃饭了,先看一种简单的实现。
首先把words
按字典序排序,从而保证字典序小的word在前,然后遍历。创建一个unordered_set
。
- 如果长度为1,就加入
s
,因为可能是其他单词的前缀,并更新res
。 - 如果这个单词
word
的前word.size()-1
项在set中,表明word可以由words
中的单词添加一个字母构成,就更新res
,并加入s
。 - 注意更新
res
的时候,只有长度大于现有的res
才更新,这样保证了,res为同长度下字典序最小的字符串。
实现如下
class Solution {
public:
string longestWord(vector<string>& words) {
string res;
unordered_set<string> s;
sort(words.begin(), words.end());
for (string word : words) {
if (word.size() == 1 || s.count(word.substr(0, word.size() - 1))) {
res = word.size() > res.size() ? word : res;
s.insert(word);
}
}
return res;
}
};
先去吃饭了,下午再补复杂的方法。
实现2
先补一些基础知识
trie:又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。Trie-维基百科
基本性质:
- 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符互不相同。
上面的Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。来源:Trie树(Prefix Tree)介绍
因此,插入所有单词,构成一个Tier即可。实现如下:
//Definition of TrieNode
class TrieNode {
public:
bool last;
vector<TrieNode*> children;
TrieNode():last(false),children(26,nullptr){}
};
class Trie {
public:
TrieNode* root;
Trie():root(new TrieNode()){}
void insert(string s) {
TrieNode* curr = root;
for (char c : s) {
int index = c - 'a';
if (!curr->children[index]) {
curr->children[index] = new TrieNode();
}
curr = curr->children[index];
}
curr->last = true;
}
void dfs(TrieNode* curr, string& res, string& s) {
if (curr->last) {
if (s.size() > res.size())
res = s;
}
for (int index = 0; index < 26;++index) {
if (curr->children[index]&& curr->children[index]->last) {
s.push_back(index + 'a');
dfs(curr->children[index], res, s);
s.pop_back();
}
}
}
string dfsSearch() {
string res, s;
dfs(root, res, s);
return res;
}
};
class Solution {
public:
string longestWord(vector<string>& words) {
Trie t;
for (string word : words)
t.insert(word);
return t.dfsSearch();
}
};
实现3
提交完了以后,还看到一种简单粗暴的方法。
对words建立一个unordered_set
,然后遍历words,对每个word,
- 若比res短,或者相同,但字典序大于等于res时,跳过
- 否则,循环去判断,每去掉一个后缀,剩余的是不是在哈希表里,如果有不在的就不满足,如果都在,则标记为新的res
class Solution {
public:
string longestWord(vector<string>& words) {
unordered_set<string> s(words.size());
for (auto &w : words) s.insert(w);
string rs;
for (auto &w : words) {
if (w.size() < rs.size() || (w.size() == rs.size() && w >= rs)) continue;
bool found = true;
string temp = w;
while (temp.size() > 1) {
temp.pop_back();
if (!s.count(temp)) { found = false; break; }
}
if (found) rs = w;
}
return rs;
}
};