Problem Statement (Asked By Twitter)
Build an autocomplete system that, given a query string s
and a set of possible query strings returns all strings from the set that start with s
as a prefix.
For instance, if the query string is de
and the set of strings is ["dog", "deer", "deal"]
, the output should be ["deer", "deal"]
.
Hint: Consider preprocessing the set of strings into a more efficient data structure to optimize query performance.
Problem link: https://leetcode.com/problems/design-search-autocomplete-system/description/
Disclaimer: Try solving the problem on your own first! Use this solution only as a reference to learn and improve.
A simple approach to solve the autocomplete problem is to iterate through a list of words and check if each word starts with the given prefix. If it does, we add it to the result set and return the results.
import java.util.ArrayList; import java.util.List; public class Autocomplete { private static final List<String> WORDS = List.of("foo", "bar", "baz", "foobar"); public static List<String> autocomplete(String prefix) { List<String> results = new ArrayList<>(); for (String word : WORDS) { if (word.startsWith(prefix)) { results.add(word); } } return results; } public static void main(String[] args) { System.out.println(autocomplete("fo")); // Output: [foo, foobar] } }
While this works, it takes O(N) time to check all words in the list, where N is the number of words.
To improve efficiency, we can preprocess the dictionary into a trie. A trie stores each word by splitting it into characters, allowing us to quickly find all words starting with a given prefix.
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; class TrieNode { Map<Character, TrieNode> children = new HashMap<>(); boolean isEndOfWord; TrieNode() { this.isEndOfWord = false; } } class Trie { private final TrieNode root; public Trie() { this.root = new TrieNode(); } // Insert a word into the trie public void insert(String word) { TrieNode node = root; for (char c : word.toCharArray()) { node.children.putIfAbsent(c, new TrieNode()); node = node.children.get(c); } node.isEndOfWord = true; } // Retrieve all words starting with the given prefix public List<String> autocomplete(String prefix) { TrieNode node = root; for (char c : prefix.toCharArray()) { if (!node.children.containsKey(c)) { return new ArrayList<>(); } node = node.children.get(c); } List<String> results = new ArrayList<>(); collectAllWords(node, prefix, results); return results; } // Helper function to collect all words under a given trie node private void collectAllWords(TrieNode node, String prefix, List<String> results) { if (node.isEndOfWord) { results.add(prefix); } for (Map.Entry<Character, TrieNode> entry : node.children.entrySet()) { collectAllWords(entry.getValue(), prefix + entry.getKey(), results); } } } public class Autocomplete { public static void main(String[] args) { Trie trie = new Trie(); String[] words = {"foo", "bar", "baz", "foobar"}; for (String word : words) { trie.insert(word); } System.out.println(trie.autocomplete("fo")); // Output: [foo, foobar] System.out.println(trie.autocomplete("ba")); // Output: [bar, baz] } }
TrieNode
).isEndOfWord
flag marks the end of a word.This implementation is far more efficient for large dictionaries and frequent prefix searches.
Did this solution help you understand the concept better? Let me know your thoughts in the comments below! Don’t forget to follow us on Instagram @coderz.py for more DSA tips and solutions to ace your coding interviews.
Find the length of the longest absolute path to a file within the abstracted file…
You manage an e-commerce website and need to keep track of the last N order…
You are given a stream of elements that is too large to fit into memory.…
The formula for the area of a circle is given by πr². Use the Monte…
Given an integer k and a string s, write a function to determine the length…
There is a staircase with N steps, and you can ascend either 1 step or…