Problem
Implement an autocomplete system. That is, given a query string s
and a set of all possible query strings, return all strings in the set that have s as a prefix.
For example, given the query string de
and the set of strings [dog, deer, deal]
, return [deer, deal]
.
Hint: Try preprocessing the dictionary into a more efficient data structure to speed up queries.
Solution
Method 1 - Using Trie with map of children and DFS
Trie Class
We have already seen Map-based Trie Implementation, and also seen another implementation of trie here: Implement Trie Problem.
Take a look at Trie class.
We have already seen most of the functions in Implement Trie Problem, and here we will cover wordsByPrefix
function.
The algorithm to fetch the word list is almost similar to the one for adding a word
- We can use the
startsWith
function to check if prefix exists in trie. If we getfalse
, we return empty list. Another way to check is we go through prefix word in the trie, and if we encounter null node, we return empty list. - Otherwise run the DFS from prefix point, and till all the leaf nodes.
Code
Java
class Trie {
static class TrieNode {
private boolean isLeaf;
private Map<Character, TrieNode> children = new HashMap<>();
}
public TrieNode root;
/** Initialize your data structure here. */
public Trie() {
this.root = new TrieNode();
}
/** Inserts a word into the trie. */
public void insert(String word) {}
/** Returns if the word is in the trie. */
public boolean search(String word) {}
public boolean startsWith(String prefix) {}
public List<String> wordsByPrefix(String prefix) {
List<String> ans = new ArrayList<>();
TrieNode curr = this.root;
// Navigate to the end of the prefix
for (char c: prefix.toCharArray()) {
if (!curr.children.containsKey(c)) {
// If prefix is not present, return empty list
return ans;
}
curr = curr.children.get(c);
}
// Recursively find all words starting with the given prefix
dfs(curr, new StringBuilder(prefix), ans);
return ans;
}
private void dfs(TrieNode node, StringBuilder path, List<String> ans) {
if (node.isLeaf) {
ans.add(path.toString());
}
for (Map.Entry<Character, TrieNode> entry: node.children.entrySet()) {
// For each child, append the char and search deeper
path.append(entry.getKey());
dfs(entry.getValue(), path, ans);
// Backtrack to previous state to explore other paths
path.deleteCharAt(path.length() - 1);
}
}
}