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();
}
}