## Prefix Matching

###### DESCRIPTION

Use a Trie to implement a prefix matching function.

- match(prefix) returns a list of all words in the Trie that start with the given prefix. The words can be in any order.

The creation of the Trie is already implemented for you.

The test cases include two parameters:

- words: a list of words to add to the Trie,
- prefix: a prefix to search for.

The test cases will create the Trie with the initial words, and then run the match command, and compare the output to the expected output.

###### EXAMPLES

**Example 1:**

Input:

`initialWords = ["apple", "app", "apartment", "ap", "apricot"] prefix = "app"`

Output: ["apple", "app"]

**Example 2:**

Input:

`initialWords = ["ball", "bath", "bat", "batter"] prefix = "bat"`

Output: ["bat", "bath", "batter"]

Run your code to see results here

## Explanation

We need to use the Trie to find all words that have a given prefix. The intuition is to search for the prefix in the trie and then perform **depth-first search** to find all words that have the given prefix.

The animation below visualizes what that looks like:

#### Step 1: Search for the Prefix

The first step is to search for the prefix in the trie. We start from the root node and traverse down the trie to find the node that corresponds to the prefix. If the prefix does not exist in the trie, we return an empty list. We stop at the node that corresponds to the prefix.

def prefix(self, word):"""Return a list of all words in the triethat start with the given prefix."""node = self.rootfor char in word:if char not in node.children:return [] # Prefix not found in trienode = node.children[char]

#### Step 2: Perform Depth-First Search

After finding the node that corresponds to the prefix, we perform a depth-first search to find all words that have the given prefix.

We can introduce a helper function dfs that takes the current node and the current word as arguments. Each call to dfs will explore the children of the current node and append the characters to the current word. When we reach the end of a word, we add the current word to the result list, which we will return at the end.

def prefix(self, word):"""Return a list of all words in the trie that start with the given prefix."""node = self.rootfor char in word:if char not in node.children:return [] # Prefix not found in trienode = node.children[char]# perform DFS from this node to find all wordsresult = []def dfs(node, current_word):if node.isEndOfWord:result.append(current_word)for char, child_node in node.children.items():dfs(child_node, current_word + char)dfs(node, word)return result

### Complexity Analysis

**Time Complexity**
The time complexity of this solution is O(N + M), where N is the length of the prefix and M is the number of characters in the words that have the given prefix. We search for the prefix in the trie in O(N) time and perform a depth-first search to find all words in O(M) time.

**Space Complexity:**
We exclude the space required to store the trie itself, as it is not part of the prefix algorithm's space complexity.

There are two parts to consider for the space complexity.

- The space required for the output list.
- The call stack space for the recursive calls to dfs.

The space complexity is be dominated by the output list because they store all words that match the given prefix, while the call stack space is only proportional to the length of the longest word that matches the prefix.

If N is the length of the prefix and M is the number of characters in the words that match the prefix, the space complexity is O(x * N + M) for the output list, where x is the number of words that match the prefix.

The exact calculation of this space complexity is not as important as understanding why the output list dominates the space complexity.

Loading comments...