题目:
难度:中等
提示
给出一个字符串数组 words
组成的一本英语词典。返回 words
中最长的一个单词,该单词是由 words
词典中其他单词逐步添加一个字母组成。
若其中有多个可行的答案,则返回答案中字典序最小的单词。若无答案,则返回空字符串。
示例 1:
输入:words = ["w","wo","wor","worl", "world"] 输出:"world" 解释: 单词"world"可由"w", "wo", "wor", 和 "worl"逐步添加一个字母组成。
示例 2:
输入:words = ["a", "banana", "app", "appl", "ap", "apply", "apple"] 输出:"apple" 解释:"apply" 和 "apple" 都能由词典中的单词组成。但是 "apple" 的字典序小于 "apply"
提示:
1 <= words.length <= 1000
1 <= words[i].length <= 30
- 所有输入的字符串
words[i]
都只包含小写字母。
面试中遇到过这道题?
1/5
是
否
通过次数
69.3K
提交次数
133.1K
通过率
52.1%
方法一:字典树
看到词典和逐步添加第一想到的就是字典树。先构建好一颗词典树,其中每一个节点的子节点用哈希表来表示。
首先创建一个空的根节点,然后将words中的单词都插入到字典树中,这样就建好了一颗字典树。随后查找最长单词用递归的方法,从根节点开始,动态维护一个递归过程中路径上的单词。当遍历到某个节点cur时,如果cur的某个孩子的end标志位是true,就代表还可以在这个孩子节点上逐步扩增单词,就递归调用这个孩子节点,路径上的单词也对应加一。路径上结果的最长的单词就是答案。
代码
class Solution {
public:
string ans="";
struct Node{
bool end=false;
char letter;
//有多个可行答案时,取字典序小的单词,所以不能用unordered_map
map<char,Node*> next;
Node(){};
Node(char c): letter(c) {};
};
void insert(string word,Node* root){
Node* cur=root;
int n=word.length();
int i=0;
//bool falg=true;//标志是否由词典中的单词逐步添加一个字母组成
while( i<n&&(cur->next).find(word[i])!=(cur->next).end() ){
cur=(cur->next)[word[i]];
//if(cur->end==false) flag=false;
i++;
}
if(i==n){
if(cur->end==false){
cur->end=true;
}else{
return;
}
}else{
while(i<n){
(cur->next)[word[i]]=new Node(word[i]);
cur=(cur->next)[word[i]];
i++;
}
cur->end=true;
}
}
string longer(string a,string b){
if(a.length()>b.length()) return a;
else if(a.length()<b.length()) return b;
else if(a<b) return a;
else return b;
}
void dfs(Node* cur,string temp){
for(auto x:cur->next){
Node* near=x.second;
if(near->end==true){
ans=longer(ans,temp+near->letter);
dfs(near,temp+near->letter);
}
}
}
string longestWord(vector<string>& words) {
Node* root=new Node();
for(auto word:words){
insert(word,root);
}
string temp="";
dfs(root,temp);
return ans;
}
};
方法二:哈希集合
写完字典树后看答案还有这种方法。
将字典words按照(长度非递减&&字典序递减)排序后,依次遍历for(auto word:words);
如果word是由 words
词典中其他单词逐步添加一个字母组成。那么word(0,word.size()-1)要么是空字符要么存在words中,而我们先前已经排序过,所以word(0,word.size()-1)要么是空字符要么存在words中并且排在word前面。所以我们只需要设置一个字符串集合,集合初始只有一个元素空字符串""。再遍历words是,如果word(0,word.size()-1)能在集合中找到,就将word加入集合,答案更新为word.
下面是官方题解代码
class Solution {
public:
string longestWord(vector<string> &words) {
sort(words.begin(), words.end(), [](const string &a, const string &b) {
return a.size() != b.size() ? a.size() < b.size() : a > b;
});
string longest;
unordered_set<string> candidates = {""};
for (const auto &word: words) {
cout<<word<<endl;
if (candidates.count(word.substr(0, word.size() - 1))) {
candidates.emplace(word);
longest = word;
}
}
return longest;
}
};