MindForge
MindForge
DashboardHelp & Support
Dashboard
MindForge

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Tries (Prefix Trees)
Course contents

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Browsing without an account — progress is saved in this browser only.Sign in to save it

Tries (Prefix Trees)

Reading120 min

Type "ca" into your phone's contacts search and it instantly shows "Carl," "Carol," and "Cathy." That's a trie at work: a tree built for exactly one job, answering "what starts with this prefix?" Autocomplete, spell-check, IP routing tables, and word-search puzzles all reduce to that same question, one a hash table answers poorly since it can only match whole keys, not prefixes. This lesson builds a trie from scratch, then uses it on two escalating problems, one of them a hard.

What a trie node holds

Each node represents one character position. It holds links to its children, one per possible next character, plus a flag marking "a word ends here."

class TrieNode:
    def __init__(self):
        self.children = {}       # char -> TrieNode
        self.is_end_of_word = False

A path from the root spelling c-a-t represents the word "cat". Words that share a prefix share nodes: "cat" and "car" both walk c -> a, then split at the third letter. That sharing is exactly what makes prefix queries cheap; you're not comparing "cat" against "car" character by character, you're walking one shared path and branching only where the words actually differ.

        root
        /
       c
       |
       a
      / \
     t   r
     *   *      (* marks is_end_of_word = True)

Remember: a trie node is a character position, not a whole word. Shared prefixes share the same nodes.

Sign in to try this knowledge check.

Insert, search, and prefix search

All three operations walk the tree one character at a time: O(m), where m is the length of the word or prefix. That cost never depends on how many other words are already stored, only on the length of what you're looking up.

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word: str) -> None:
        node = self.root
        for ch in word:
            if ch not in node.children:
                node.children[ch] = TrieNode()
            node = node.children[ch]
        node.is_end_of_word = True

    def _find_node(self, prefix: str):
        node = self.root
        for ch in prefix:
            if ch not in node.children:
                return None
            node = node.children[ch]
        return node

    def search(self, word: str) -> bool:
        node = self._find_node(word)
        return node is not None and node.is_end_of_word

    def startsWith(self, prefix: str) -> bool:
        return self._find_node(prefix) is not None

The detail that trips people up: search needs is_end_of_word == True at the final node, meaning the exact word was inserted. startsWith only needs the path to exist; some word starting with this prefix was inserted, but the prefix itself might not be a whole word on its own. Mixing these two up is the most common trie bug.

Remember: search needs the path AND the end-of-word flag. startsWith needs only the path.

Sign in to try this knowledge check.

Trading memory for speed

A naive trie node using a fixed 26-slot array (children = [None] * 26) wastes memory whenever the alphabet in a given subtree is small or sparse. A dict-based node (children = {}) only allocates entries for characters actually present, at the cost of slightly slower lookups: hashing instead of a direct array index.

Array of 26 Dict
Lookup O(1), direct index O(1) average, hash overhead
Space per node Fixed 26 slots always Only used characters
Best for Lowercase-only, dense tries Unicode, sparse tries, unknown alphabet

A further optimization for memory-constrained cases is the compressed trie (radix tree): it merges chains of single-child nodes into one edge labeled with a substring instead of one character, cutting node count sharply for tries with many long, unique suffixes. You're rarely asked to implement this in an interview, but naming it is worth doing if asked how you'd scale a trie to millions of entries.

Remember: array children are faster but always cost 26 slots; dict children only pay for characters actually used.

Sign in to try this knowledge check.

Implement Trie

LeetCode 208 — Trie — Basic implementation

This problem is the concept section above, asked as a standalone exercise: build insert, search, and startsWith directly.

Approach: TrieNode holds a children dict and an end-of-word flag. insert walks or creates nodes as needed. search and startsWith walk and check existence; search additionally checks the end-of-word flag.

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word: str) -> None:
        node = self.root
        for ch in word:
            node = node.children.setdefault(ch, TrieNode())
        node.is_end_of_word = True

    def search(self, word: str) -> bool:
        node = self.root
        for ch in word:
            if ch not in node.children:
                return False
            node = node.children[ch]
        return node.is_end_of_word

    def startsWith(self, prefix: str) -> bool:
        node = self.root
        for ch in prefix:
            if ch not in node.children:
                return False
            node = node.children[ch]
        return True

Complexity: insert/search/startsWith are all O(m) time, where m is the word or prefix length. Space O(total characters across all inserted words) in the worst case, when no prefixes are shared.

Common mistakes: Returning True from search just because the path exists, forgetting the end-of-word check. Also, unconditionally writing node.children[ch] = TrieNode() on insert instead of checking first, which wipes out existing subtrees and loses previously inserted words.

Remember: this problem IS the concept section. If you can write insert/search/startsWith from memory, you know tries.

Sign in to try this knowledge check.

Word Search II

LeetCode 212 — Trie + Backtracking — Hard

Searching the board separately for each word in a word list, one full DFS per word, is far too slow when both the board and the word list are large. Instead, build one trie from every word, then run a single DFS pass over the board, walking the trie and the board together, step for step. This shares work across words with common prefixes and lets you prune a board path the instant it stops matching any word's prefix.

Approach: Build a trie from words (mark word ends). DFS from every board cell; at each step, only continue if the current character exists as a trie child of the current trie node. When a trie node marks a complete word, record it (and mark that trie node as spent, to avoid duplicate results). Backtrack the board cell after exploring.

def findWords(board: list[list[str]], words: list[str]) -> list[str]:
    root = TrieNode()
    for word in words:
        node = root
        for ch in word:
            node = node.children.setdefault(ch, TrieNode())
        node.is_end_of_word = True
        node.word = word  # stash the full word at the terminal node

    rows, cols = len(board), len(board[0])
    result = []

    def dfs(r, c, node):
        ch = board[r][c]
        if ch not in node.children:
            return
        nxt = node.children[ch]
        if nxt.is_end_of_word:
            result.append(nxt.word)
            nxt.is_end_of_word = False  # avoid duplicate matches

        board[r][c] = '#'  # mark visited in place
        for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
            nr, nc = r + dr, c + dc
            if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] != '#':
                dfs(nr, nc, nxt)
        board[r][c] = ch  # backtrack

        if not nxt.children:  # prune dead trie branches for efficiency
            node.children.pop(ch)

    for r in range(rows):
        for c in range(cols):
            dfs(r, c, root)

    return result

Complexity: Time O(rows × cols × 4^L) in the worst case (L = longest word length), but trie pruning, stopping as soon as no word's prefix matches and removing exhausted branches, makes it far faster in practice than a per-word DFS. Space O(total trie nodes + recursion depth).

Common mistakes: Running a separate DFS search per word instead of one shared trie-guided DFS. Correct, but too slow for the hard-tier constraints. Forgetting to backtrack the board mutation after recursion, which corrupts later searches. Skipping the guard against duplicate results when the same word can be found via multiple paths; resetting the end-of-word flag after the first match handles this.

Remember: one trie built from all words, one shared DFS over the board, pruning dead branches as you go.

Sign in to try this knowledge check.

Replace Words

LeetCode 648 — Trie

For each word in a sentence, find its shortest dictionary root, if one exists, and swap the word for that root. A trie built from the roots lets you walk each word one character at a time and stop at the very first end-of-word flag you hit, which is automatically the shortest matching root.

Approach: Insert all roots into a trie. For each word in the sentence, walk the trie; the moment you hit an end-of-word flag (or run out of characters or trie path), replace the word with the prefix walked so far, or keep the original word if no root matched.

def replaceWords(dictionary: list[str], sentence: str) -> str:
    root_trie = TrieNode()
    for root in dictionary:
        node = root_trie
        for ch in root:
            node = node.children.setdefault(ch, TrieNode())
        node.is_end_of_word = True

    def find_root(word: str) -> str:
        node = root_trie
        prefix = []
        for ch in word:
            if ch not in node.children:
                return word  # no matching root, keep original
            prefix.append(ch)
            node = node.children[ch]
            if node.is_end_of_word:
                return "".join(prefix)
        return word

    return " ".join(find_root(word) for word in sentence.split())

Complexity: Time O(total characters in the dictionary + total characters in the sentence), space O(total characters in the dictionary) for the trie.

Common mistakes: Not stopping at the first end-of-word flag reached. The problem wants the shortest matching root, so continuing past it would wrongly look for a longer one. Also, using a naive "try every root as a prefix" approach with string methods, which is O(words × roots × root length) instead of the trie's near-linear scan.

Remember: stop at the first end-of-word flag you hit while walking down; that's always the shortest matching root.

Sign in to try this knowledge check.

Maximum XOR of Two Numbers in an Array

LeetCode 421 — Trie — Hard

To maximize the XOR of two numbers, you want their bits to differ at the highest positions possible, since a higher bit position contributes more to the result. A binary trie, where each node has at most 2 children (bit 0 or bit 1), lets you, for each number, greedily walk toward the opposite bit at every position. That greedy walk finds the best XOR partner for that number in O(32) steps instead of comparing it against every other number. It's the same prefix-tree idea from earlier in this lesson, applied to bits instead of letters.

Approach: Insert every number into a binary trie, most-significant-bit first (fixed 32-bit width). For each number, walk the trie trying to go the opposite direction of each of its bits; when the opposite branch doesn't exist, take the only branch available. Track the best XOR found.

class BinaryTrieNode:
    def __init__(self):
        self.children = {}  # 0 or 1 -> BinaryTrieNode

def findMaximumXOR(nums: list[int]) -> int:
    root = BinaryTrieNode()
    BITS = 31  # enough for LeetCode's constraint (nums < 2^31)

    def insert(num):
        node = root
        for i in range(BITS, -1, -1):
            bit = (num >> i) & 1
            node = node.children.setdefault(bit, BinaryTrieNode())

    def query(num):
        node = root
        xor = 0
        for i in range(BITS, -1, -1):
            bit = (num >> i) & 1
            desired = 1 - bit  # the opposite bit maximizes this position's contribution
            if desired in node.children:
                xor |= (1 << i)
                node = node.children[desired]
            else:
                node = node.children[bit]
        return xor

    for num in nums:
        insert(num)

    return max(query(num) for num in nums)

Complexity: Time O(n × 32) = O(n), space O(n × 32) for the trie nodes. Far better than the naive O(n²) pairwise XOR comparison.

Common mistakes: Iterating bits least-significant-first instead of most-significant-first. The greedy "prefer the opposite bit" strategy only produces the true maximum when higher bit positions are locked in before lower ones. Also, forgetting a fixed bit width, which lets negative numbers or inconsistent trie depths break the comparisons.

Remember: a binary trie over bits, most significant first, walking toward the opposite bit at every step, turns "best XOR partner" into an O(32) walk.

Sign in to try this knowledge check.
PreviousBinary Search Trees and Rebuilding a TreeNextHeaps and Priority Queues

Course progress

0%
0 of 177 modules complete

On this page

  • What a trie node holds
  • Insert, search, and prefix search
  • Trading memory for speed
  • Implement Trie
  • Word Search II
  • Replace Words
  • Maximum XOR of Two Numbers in an Array