Longest Word in Dictionary Using Trie in DSA Java - Time & Space Complexity
We want to understand how the time needed to find the longest word grows as the dictionary size increases.
Specifically, how the operations scale when using a Trie to find the longest word built from other words.
Analyze the time complexity of the following code snippet.
class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isWord = false;
}
void insert(String word, TrieNode root) {
TrieNode node = root;
for (char c : word.toCharArray()) {
int idx = c - 'a';
if (node.children[idx] == null) node.children[idx] = new TrieNode();
node = node.children[idx];
}
node.isWord = true;
}
This code inserts words into a Trie, marking nodes where words end.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Inserting each word character by character into the Trie.
- How many times: For each of the n words, we loop through its length (let's call average length m).
- Also, searching for the longest word involves traversing nodes, but insertion dominates.
As the number of words grows, and their lengths grow, the operations increase proportionally.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 words, avg length 5 | ~50 insert steps |
| 100 words, avg length 5 | ~500 insert steps |
| 1000 words, avg length 5 | ~5000 insert steps |
Pattern observation: Operations grow roughly linearly with total characters inserted (n * m).
Time Complexity: O(n * m)
This means the time grows linearly with the number of words and their average length.
[X] Wrong: "Inserting words into a Trie is always O(n²) because of nested loops."
[OK] Correct: Each word is processed once character by character, so total work is proportional to total characters, not squared.
Understanding this helps you explain how Tries efficiently handle prefix-based problems and why their insertion scales well with input size.
"What if we used a hash set instead of a Trie for checking prefixes? How would the time complexity change?"
Practice
Solution
Step 1: Understand Trie usage
A Trie stores words by their prefixes, allowing quick prefix checks.Step 2: Relate to longest word search
Finding the longest word with all prefixes present is easier with Trie prefix checks.Final Answer:
To efficiently store and search words by their prefixes -> Option AQuick Check:
Trie = prefix search [OK]
- Confusing Trie with sorting structures
- Thinking Trie reverses words
- Assuming Trie counts words only
Solution
Step 1: Understand character indexing
Characters are converted to index by subtracting 'a' to map 'a' to 0, 'b' to 1, etc.Step 2: Check Java syntax for charAt
Java uses word.charAt(i) to get character at index i.Final Answer:
node.children[word.charAt(i) - 'a'] = new TrieNode(); -> Option BQuick Check:
Correct char indexing = node.children[word.charAt(i) - 'a'] = new TrieNode(); [OK]
- Using word[i] instead of charAt(i)
- Adding 'a' instead of subtracting
- Using char directly without index conversion
String[] words = {"a", "app", "ap", "apply", "apple"};
LongestWordInDictionaryUsingTrie trie = new LongestWordInDictionaryUsingTrie();
System.out.println(trie.longestWord(words));Solution
Step 1: Insert all words into Trie
Words inserted: "a", "app", "ap", "apply", "apple".Step 2: Check longest word with all prefixes present
"app" is longest where all prefixes "a", "ap", "app" exist. "apple" and "apply" prefixes "appl" missing.Final Answer:
"app" -> Option AQuick Check:
Longest word with all prefixes = "app" [OK]
- Choosing longest word ignoring prefix condition
- Confusing "apple" and "apply" as valid
- Ignoring shorter prefix words
public String longestWord(String[] words) {
TrieNode root = new TrieNode();
for (String w : words) insert(root, w);
String result = "";
dfs(root, new StringBuilder(), result);
return result;
}
private void dfs(TrieNode node, StringBuilder path, String result) {
if (node == null) return;
if (!node.isWord && path.length() != 0) return;
if (path.length() > result.length()) result = path.toString();
for (int i = 0; i < 26; i++) {
if (node.children[i] != null) {
path.append((char)(i + 'a'));
dfs(node.children[i], path, result);
path.deleteCharAt(path.length() - 1);
}
}
}Solution
Step 1: Analyze result parameter usage
String is immutable and passed by value, so updating result inside dfs does not affect outer variable.Step 2: Understand effect on final output
Result remains empty because changes inside dfs do not persist outside recursion.Final Answer:
The result string is not updated correctly inside dfs due to pass-by-value -> Option CQuick Check:
String immutability causes result update failure [OK]
- Assuming String updates persist in recursion
- Ignoring missing insert method
- Overlooking null checks in children
Solution
Step 1: Traverse children in alphabetical order
DFS visits children from 'a' to 'z' ensuring lex order traversal.Step 2: Update result conditionally
Update result only if new word is longer or same length but lex smaller.Step 3: Ensure final result is lex smallest longest word
This approach naturally picks lex smallest among longest words.Final Answer:
Traverse children in alphabetical order during DFS and update result only if new word is longer or lex smaller -> Option DQuick Check:
Lex order DFS + conditional update = Traverse children in alphabetical order during DFS and update result only if new word is longer or lex smaller [OK]
- Sorting input but not controlling DFS order
- Using data structures that add unnecessary complexity
- Ignoring lex order during traversal
Ready to prove it?
Pass the test and this pattern moves unknown
