💡 Using a Trie explicitly models prefix relationships and BFS traversal ensures we find the longest valid word with all prefixes present, optimizing prefix checks.
Intuition
Build a Trie from all words. Use BFS to traverse nodes only if the node marks a complete word, ensuring all prefixes exist. Track the longest word found.
Algorithm
- Build a Trie inserting all words, marking nodes that end words.
- Use a queue to perform BFS starting from root.
- At each node, enqueue child nodes only if they mark complete words.
- Track the longest word found during BFS traversal.
💡 BFS ensures we explore words level by level, guaranteeing prefixes exist before children are visited.
from collections import deque
class TrieNode:
def __init__(self):
self.children = {}
self.word = None
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for ch in word:
if ch not in node.children:
node.children[ch] = TrieNode()
node = node.children[ch]
node.word = word
def longestWord(words):
trie = Trie()
for word in words:
trie.insert(word)
queue = deque([trie.root])
longest = ""
while queue:
node = queue.popleft()
for ch in sorted(node.children.keys()): # changed to normal lex order
child = node.children[ch]
if child.word is not None:
queue.append(child)
if len(child.word) > len(longest) or (len(child.word) == len(longest) and child.word < longest):
longest = child.word
return longest
# Driver code
if __name__ == "__main__":
words = ["w","wo","wor","worl","world"]
print(longestWord(words)) # Output: world
Line Notes
self.children = {}Dictionary to store child nodes for each character
self.word = NoneStore complete word at node if it ends here
for ch in word:Insert characters one by one into Trie
node.word = wordMark node as end of a word
queue = deque([trie.root])Start BFS from root node
for ch in sorted(node.children.keys()):Traverse children in lex order to ensure lex smallest word found first
if child.word is not None:Only enqueue nodes that mark complete words
if len(child.word) > len(longest) or (len(child.word) == len(longest) and child.word < longest):Update longest word by length and lex order
import java.util.*;
class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
String word = null;
}
public class Solution {
TrieNode root = new TrieNode();
public void insert(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
node.children.putIfAbsent(ch, new TrieNode());
node = node.children.get(ch);
}
node.word = word;
}
public String longestWord(String[] words) {
for (String w : words) insert(w);
Queue<TrieNode> queue = new LinkedList<>();
queue.offer(root);
String longest = "";
while (!queue.isEmpty()) {
TrieNode node = queue.poll();
List<Character> keys = new ArrayList<>(node.children.keySet());
Collections.sort(keys); // changed to normal lex order
for (char ch : keys) {
TrieNode child = node.children.get(ch);
if (child.word != null) {
queue.offer(child);
if (child.word.length() > longest.length() || (child.word.length() == longest.length() && child.word.compareTo(longest) < 0)) {
longest = child.word;
}
}
}
}
return longest;
}
public static void main(String[] args) {
Solution sol = new Solution();
String[] words = {"w","wo","wor","worl","world"};
System.out.println(sol.longestWord(words)); // Output: world
}
}
Line Notes
Map<Character, TrieNode> children = new HashMap<>();Map to store child nodes for each character
String word = null;Store complete word at node if it ends here
for (char ch : word.toCharArray()) {Insert characters one by one into Trie
node.word = word;Mark node as end of a word
Queue<TrieNode> queue = new LinkedList<>();Use queue for BFS traversal
Collections.sort(keys);Traverse children in lex order to ensure lex smallest word found first
if (child.word != null) {Only enqueue nodes that mark complete words
if (child.word.length() > longest.length() || (child.word.length() == longest.length() && child.word.compareTo(longest) < 0)) {Update longest word by length and lex order
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <algorithm>
using namespace std;
struct TrieNode {
unordered_map<char, TrieNode*> children;
string word = "";
};
class Trie {
public:
TrieNode* root;
Trie() { root = new TrieNode(); }
void insert(const string& word) {
TrieNode* node = root;
for (char ch : word) {
if (!node->children.count(ch))
node->children[ch] = new TrieNode();
node = node->children[ch];
}
node->word = word;
}
};
string longestWord(vector<string>& words) {
Trie trie;
for (auto& w : words) trie.insert(w);
queue<TrieNode*> q;
q.push(trie.root);
string longest = "";
while (!q.empty()) {
TrieNode* node = q.front(); q.pop();
vector<char> keys;
for (auto& p : node->children) keys.push_back(p.first);
sort(keys.begin(), keys.end()); // changed to normal lex order
for (char ch : keys) {
TrieNode* child = node->children[ch];
if (!child->word.empty()) {
q.push(child);
if (child->word.size() > longest.size() || (child->word.size() == longest.size() && child->word < longest)) {
longest = child->word;
}
}
}
}
return longest;
}
int main() {
vector<string> words = {"w","wo","wor","worl","world"};
cout << longestWord(words) << endl; // Output: world
return 0;
}
Line Notes
unordered_map<char, TrieNode*> children;Map to store child nodes for each character
string word = "";Store complete word at node if it ends here
for (char ch : word) {Insert characters one by one into Trie
node->word = word;Mark node as end of a word
queue<TrieNode*> q;Queue for BFS traversal
sort(keys.begin(), keys.end());Traverse children in lex order to ensure lex smallest word found first
if (!child->word.empty()) {Only enqueue nodes that mark complete words
if (child->word.size() > longest.size() || (child->word.size() == longest.size() && child->word < longest)) {Update longest word by length and lex order
class TrieNode {
constructor() {
this.children = new Map();
this.word = null;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let node = this.root;
for (const ch of word) {
if (!node.children.has(ch)) {
node.children.set(ch, new TrieNode());
}
node = node.children.get(ch);
}
node.word = word;
}
}
function longestWord(words) {
const trie = new Trie();
for (const w of words) trie.insert(w);
const queue = [trie.root];
let longest = "";
while (queue.length > 0) {
const node = queue.shift();
const keys = Array.from(node.children.keys()).sort(); // changed to normal lex order
for (const ch of keys) {
const child = node.children.get(ch);
if (child.word !== null) {
queue.push(child);
if (child.word.length > longest.length || (child.word.length === longest.length && child.word < longest)) {
longest = child.word;
}
}
}
}
return longest;
}
// Test
console.log(longestWord(["w","wo","wor","worl","world"])); // Output: world
Line Notes
this.children = new Map();Map to store child nodes for each character
this.word = null;Store complete word at node if it ends here
for (const ch of word) {Insert characters one by one into Trie
node.word = word;Mark node as end of a word
const queue = [trie.root];Initialize queue for BFS traversal
const keys = Array.from(node.children.keys()).sort();Traverse children in lex order to ensure lex smallest word found first
if (child.word !== null) {Only enqueue nodes that mark complete words
if (child.word.length > longest.length || (child.word.length === longest.length && child.word < longest)) {Update longest word by length and lex order
TimeO(n * m + n * 26)
SpaceO(n * m)
Building the Trie takes O(n*m). BFS visits nodes with words only, up to n nodes, each with up to 26 children.
💡 For n=100000 and m=30, this approach is efficient and scales well due to Trie structure and BFS pruning.
Interview Verdict: Accepted
This is the optimal approach for prefix-related problems, demonstrating mastery of Trie and BFS.