原文链接: https://leetcode-cn.com/problems/longest-word-in-dictionary
英文原文
Given an array of strings words
representing an English Dictionary, return 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".
Constraints:
1 <= words.length <= 1000
1 <= words[i].length <= 30
words[i]
consists of lowercase English letters.
中文题目
给出一个字符串数组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"。
提示:
- 所有输入的字符串都只包含小写字母。
words
数组长度范围为[1,1000]
。words[i]
的长度范围为[1,30]
。
通过代码
官方题解
方法一:暴力法
对于每个单词,我们可以检查它的全部前缀是否存在,可以通过 Set
数据结构来加快查找
算法:
- 当我们找到一个单词它的长度更长且它的全部前缀都存在,我们将更改答案。
- 或者,我们可以事先将单词排序,这样当我们找到一个符合条件的单词就可以认定它是答案。
[ ]class Solution(object): def longestWord(self, words): ans = "" wordset = set(words) for word in words: if len(word) > len(ans) or len(word) == len(ans) and word < ans: if all(word[:k] in wordset for k in xrange(1, len(word))): ans = word return ans
[ ]class Solution(object): def longestWord(self, words): wordset = set(words) words.sort(key = lambda c: (-len(c), c)) for word in words: if all(word[:k] in wordset for k in xrange(1, len(word))): return word return ""
[ ]class Solution { public String longestWord(String[] words) { String ans = ""; Set<String> wordset = new HashSet(); for (String word: words) wordset.add(word); for (String word: words) { if (word.length() > ans.length() || word.length() == ans.length() && word.compareTo(ans) < 0) { boolean good = true; for (int k = 1; k < word.length(); ++k) { if (!wordset.contains(word.substring(0, k))) { good = false; break; } } if (good) ans = word; } } return ans; } }
[ ]class Solution { public String longestWord(String[] words) { Set<String> wordset = new HashSet(); for (String word: words) wordset.add(word); Arrays.sort(words, (a, b) -> a.length() == b.length() ? a.compareTo(b) : b.length() - a.length()); for (String word: words) { boolean good = true; for (int k = 1; k < word.length(); ++k) { if (!wordset.contains(word.substring(0, k))) { good = false; break; } } if (good) return word; } return ""; } }
复杂度分析
- 时间复杂度:$O(\sum w_i^2)$。$w_i$ 指的是
words[i]
的长度,在Set
中检查words[i]
全部前缀是否均存在的时间复杂度是 $O(\sum w_i^2)$。 - 空间复杂度:$O(\sum w_i^2)$ 用来存放子串的空间。
方法二:前缀树 + 深度优先搜索
由于涉及到字符串的前缀,通常可以使用 trie
(前缀树)来解决。
算法:
- 将所有单词插入
trie
,然后从trie
进行深度优先搜索,每找到一个单词表示该单词的全部前缀均存在,我们选取长度最长的单词。 - 在 python 中,我们使用了 defaultdict 的方法。而在 java 中,我们使用了更通用的面向对象方法。
[ ]class Solution(object): def longestWord(self, words): Trie = lambda: collections.defaultdict(Trie) trie = Trie() END = True for i, word in enumerate(words): reduce(dict.__getitem__, word, trie)[END] = i stack = trie.values() ans = "" while stack: cur = stack.pop() if END in cur: word = words[cur[END]] if len(word) > len(ans) or len(word) == len(ans) and word < ans: ans = word stack.extend([cur[letter] for letter in cur if letter != END]) return ans
[ ]class Solution { public String longestWord(String[] words) { Trie trie = new Trie(); int index = 0; for (String word: words) { trie.insert(word, ++index); //indexed by 1 } trie.words = words; return trie.dfs(); } } class Node { char c; HashMap<Character, Node> children = new HashMap(); int end; public Node(char c){ this.c = c; } } class Trie { Node root; String[] words; public Trie() { root = new Node('0'); } public void insert(String word, int index) { Node cur = root; for (char c: word.toCharArray()) { cur.children.putIfAbsent(c, new Node(c)); cur = cur.children.get(c); } cur.end = index; } public String dfs() { String ans = ""; Stack<Node> stack = new Stack(); stack.push(root); while (!stack.empty()) { Node node = stack.pop(); if (node.end > 0 || node == root) { if (node != root) { String word = words[node.end - 1]; if (word.length() > ans.length() || word.length() == ans.length() && word.compareTo(ans) < 0) { ans = word; } } for (Node nei: node.children.values()) { stack.push(nei); } } } return ans; } }
复杂度分析
- 时间复杂度:$O(\sum w_i)$。$w_i$ 指的是
words[i]
的长度。该时间复杂度用于创建前缀树和查找单词。
如果我们使用一个 BFS 代替 DFS,并在数组中对子节点进行排序,我们就可以不必检查每个节点上的候选词是否比答案好,最佳答案将是最后访问的节点。
- 空间复杂度:$O(\sum w_i)$,前缀树所使用的空间。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
22218 | 45913 | 48.4% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|
相似题目
题目 | 难度 |
---|---|
通过删除字母匹配到字典里最长单词 | 中等 |
实现一个魔法字典 | 中等 |