[LeetCode] 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.

If there is no answer, return the empty string.

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].

 

这道题给了我们一个字典,是个字符串数组,然后问我们从单个字符开始拼,最长能组成啥单词,注意中间生成的字符串也要在字典中存在,而且当组成的单词长度相等时,返回字母顺序小的那个。好,看到这么多前缀一样多字符串,是不是很容易想到用前缀树来做,其实我们并不需要真正的建立前缀树结点,可以借鉴查找的思想来做。那么为了快速的查找某个单词是否在字典中存在,我们将所有单词放到哈希集合中,在查找的时候,可以采用BFS或者DFS都行。先来看BFS的做法,使用一个queue来辅助,我们先把所有长度为1的单词找出排入queue中,当作种子选手,然后我们进行循环,每次从队首取一个元素出来,如果其长度大于我们维护的最大值mxLen,则更新mxLen和结果res,如果正好相等,也要更新结果res,取字母顺序小的那个。然后我们试着增加长度,做法就是遍历26个字母,将每个字母都加到单词后面,然后看是否在字典中存在,存在的话,就加入queue中等待下一次遍历,完了以后记得要恢复状态,参见代码如下:

 

解法一:

复制代码
class Solution {
public:
    string longestWord(vector<string>& words) {
        string res = "";
        int mxLen = 0;
        unordered_set<string> s(words.begin(), words.end());
        queue<string> q;
        for (string word : words) {
            if (word.size() == 1) q.push(word);
        }
        while (!q.empty()) {
            string t = q.front(); q.pop();
            if (t.size() > mxLen) {
                mxLen = t.size();
                res = t;
            } else if (t.size() == mxLen) {
                res = min(res, t);
            }
            for (char c = 'a'; c <= 'z'; ++c) {
                t.push_back(c);
                if (s.count(t)) q.push(t);
                t.pop_back();
            }
        }
        return res;
    }
};
复制代码

 

下面来看递归的解法,前面都一样,不同在于直接对长度为1的单词调用递归函数,在递归中,还是先判断单词和mxLen关系来更新结果res,然后就是遍历所有字符,加到单词后面,如果在集合中存在,调用递归函数,结束后恢复状态,参见代码如下:

 

解法二:

复制代码
class Solution {
public:
    string longestWord(vector<string>& words) {
        string res = "";
        int mxLen = 0;
        unordered_set<string> s(words.begin(), words.end());for (string word : words) {
            if (word.size() == 1) helper(s, word, mxLen, res);
        }
        return res;
    }
    void helper(unordered_set<string>& s, string word, int& mxLen, string& res) {
        if (word.size() > mxLen) {
            mxLen = word.size();
            res = word;
        } else if (word.size() == mxLen) {
            res = min(res, word);
        }
        for (char c = 'a'; c <= 'z'; ++c) {
            word.push_back(c);
            if (s.count(word)) helper(s, word, mxLen, res);
            word.pop_back();
        }
    }
};
复制代码

 

下面这种解法是论坛上的高分解法,其实我们只要给数组排个序,就可以使用贪婪算法来做了,并不需要什么DFS或BFS这么复杂。首先建立一个空的哈希set,然后我们直接遍历排序后的字典,对于当前的单词,如果当前单词长度为1,或者该单词去掉最后一个字母后在集合中存在,这也不难理解,长度为1,说明是起始单词,不需要的多余的判断,否则的话就要判断其去掉最后一个字母后的单词是否在集合中存在,存在的话,才说明其中间单词都存在,因为此时是从短单词向长单词遍历,只要符合要求的才会加入集合,所以一旦其去掉尾字母的单词存在的话,那么其之前所有的中间情况都会在集合中存在。我们更新结果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;
    }
};
复制代码

 

类似题目:

Longest Word in Dictionary through Deleting

Implement Magic Dictionary

 

参考资料:

https://discuss.leetcode.com/topic/109643/java-c-clean-code

本文转自博客园Grandyang的博客,原文链接:[LeetCode] Longest Word in Dictionary 字典中的最长单词

,如需转载请自行联系原博主。

相关文章
|
3月前
Leetcode(最后一个单词长度)
这篇文章介绍了两种解决LeetCode第58题的方法,即计算给定字符串中最后一个单词的长度,方法包括翻转字符串和逆向遍历统计。
24 0
|
3月前
【LeetCode 20】151.反转字符串里的单词
【LeetCode 20】151.反转字符串里的单词
28 0
|
5月前
|
算法
LeetCode第58题最后一个单词的长度
LeetCode第58题"最后一个单词的长度"的解题方法,通过从字符串末尾向前遍历并计数非空格字符,直接得出最后一个单词的长度。
LeetCode第58题最后一个单词的长度
|
5月前
|
算法 JavaScript Python
【Leetcode刷题Python】79. 单词搜索和剑指 Offer 12. 矩阵中的路径
Leetcode第79题"单词搜索"的Python解决方案,使用回溯算法在给定的二维字符网格中搜索单词,判断单词是否存在于网格中。
66 4
|
5月前
|
Python
【Leetcode刷题Python】生词本单词整理
文章提供了一个Python程序,用于帮助用户整理和排版生词本上的单词,包括去除重复单词、按字典序排序,并按照特定的格式要求进行打印排版。
50 3
|
4月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
5月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
67 6
|
5月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
133 2
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
60 1
|
4月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口