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]
.
本质上是trie!前缀树!
class Solution(object):
def longestWord(self, words):
"""
:type words: List[str]
:rtype: str
"""
# use greey algo
words_set = set([""])
words.sort()
ans = ""
for word in words:
if word[:-1] in words_set:
if len(word) > len(ans):
ans = word
words_set.add(word)
return ans
class Node(object): def __init__(self, val=""): self.val = val self.subs = collections.defaultdict(Node) class Trie(object): def __init__(self): self.root = Node("") def insert(self, s): node = self.root for c in s: node = node.subs[c] node.val = s def longest_word(self): self.ans = "" def dfs(node): for k, n in node.subs.items(): if n.val: if len(n.val)>len(self.ans) or (len(n.val)==len(self.ans) and n.val<self.ans): self.ans = n.val dfs(n) dfs(self.root) return self.ans class Solution(object): def longestWord(self, words): """ :type words: List[str] :rtype: str """ trie = Trie() for word in words: trie.insert(word) return trie.longest_word()
注意:Trie每个结点的子树的根节点的组织方式有几种。
1>如果默认包含所有字符集,则查找速度快但浪费空间(特别是靠近树底部叶子)。
2>如果用链接法(如左儿子右兄弟),则节省空间但查找需顺序(部分)遍历链表。
3>alphabet reduction: 减少字符宽度以减少字母集个数。
4>对字符集使用bitmap,再配合链接法。
Trie的插入和查找算法:
def find(node, key): for char in key: if char in node.children: node = node.children[char] else: return None return node.value == key
algorithm insert(root : node, s : string, value : any): node = root i = 0 n = length(s) while i < n: if node.child(s[i]) != nil: node = node.child(s[i]) i = i + 1 else: break (* append new nodes, if necessary *) while i < n: node.child(s[i]) = new node node = node.child(s[i]) i = i + 1 node.value = value
class TrieNode(object): def __init__(self): self.children=collections.defaultdict(TrieNode) self.isEnd=False self.word ='' class Trie(object): def __init__(self): self.root=TrieNode() def insert(self, word): node=self.root for c in word: node =node.children[c] node.isEnd=True node.word=word def bfs(self): q=collections.deque([self.root]) res='' while q: cur=q.popleft() for n in cur.children.values(): if n.isEnd: q.append(n) if len(n.word)>len(res) or n.word<res: res=n.word return res class Solution(object): def longestWord(self, words): trie = Trie() for w in words: trie.insert(w) return trie.bfs()
java的解法:
Build a trie in the normal way, then do a dfs to find the longest continuous downward path from the root. This is not a particularly hard question in the context of trie, the point of this solution is to present a generic way of trie building and inserting that can be easily adapted to similar questions. Code:
class Solution {
public String longestWord(String[] words) { TrieNode root = new TrieNode (); root.word = "-"; for (String word : words) root.insert (word); return dfs (root, ""); } String dfs (TrieNode node, String accum) { if (node == null || node.word.length () == 0) return accum; String res = ""; if (!node.word.equals ("-")) accum = node.word; for (TrieNode child : node.links) { String curRes = dfs (child, accum); if (curRes.length () > res.length () || (curRes.length () == res.length () && curRes.compareTo (res) < 0)) res = curRes; } return res; } /* Hand write this class every time you need to so you can remember well */ static class TrieNode { String word = ""; TrieNode[] links = new TrieNode[26]; void insert (String s) { char[] chs = s.toCharArray (); TrieNode curNode = this; for (int i = 0; i < chs.length; i++) { int index = chs[i] - 'a'; if (curNode.links[index] == null) curNode.links[index] = new TrieNode (); curNode = curNode.links[index]; } curNode.word = s; } } }
A typical trie for the list of "ab", "ac"
:
class Solution {
class TrieNode {
TrieNode[] children;
boolean isWord;
String word;
public TrieNode() {
children = new TrieNode[26]; } } class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public void insert(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { int idx = word.charAt(i) - 'a'; if (node.children[idx] == null) { node.children[idx] = new TrieNode(); } node = node.children[idx]; } node.isWord = true; node.word = word; } public String findLongestWord() { String result = null; Queue<TrieNode> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0; i < size; i++) { TrieNode node = queue.poll(); for (int j = 25; j >= 0; j--) { if (node.children[j] != null && node.children[j].isWord) { result = node.children[j].word; queue.offer(node.children[j]); } } } } return result; } } public String longestWord(String[] words) { Trie trie = new Trie(); for (String word : words) { trie.insert(word); } return trie.findLongestWord(); } }