Word Search II – Solution & Complexity

Solution Walkthrough

  1. Step 1: Spot the repeated work

    • Running a separate Word Search DFS for each candidate word repeats the same board-prefix checks over and over.
    • The natural shared structure is a trie: words with the same prefix should share the same search prefix too.
  2. Step 2: Brute-force baseline

    • The brute-force strategy is: for each word, run the usual backtracking search from every board cell.
    • That can be acceptable for one word, but with thousands of words it multiplies the board search cost by the dictionary size.
  3. Step 3: Trie + DFS pruning

    • Insert every word into a trie. While traversing the board, stop immediately when the current path no longer matches any trie edge.
    • Whenever a trie node marks a complete word, add it to the answer and clear that marker so duplicates are not reported twice.
    • Sort the final answer lexicographically before returning so every language produces the same deterministic output.
  4. Step 4: Final solution (all languages)

    The trie shares prefix work across all words, which is the key improvement over checking each word independently.

    def find_words(board: list[str], words: list[str]) -> list[str]:
        trie = {}
        end = "#"
        for word in words:
            node = trie
            for char in word:
                node = node.setdefault(char, {})
            node[end] = word
    
        rows = len(board)
        cols = len(board[0])
        grid = [list(row) for row in board]
        found = []
    
        def dfs(r: int, c: int, node: dict) -> None:
            if not (0 <= r < rows and 0 <= c < cols):
                return
            char = grid[r][c]
            if char == "#" or char not in node:
                return
    
            nxt = node[char]
            word = nxt.pop(end, None)
            if word is not None:
                found.append(word)
    
            grid[r][c] = "#"
            dfs(r + 1, c, nxt)
            dfs(r - 1, c, nxt)
            dfs(r, c + 1, nxt)
            dfs(r, c - 1, nxt)
            grid[r][c] = char
    
        for row in range(rows):
            for col in range(cols):
                dfs(row, col, trie)
    
        return sorted(found)
  5. Step 5: Common mistakes and follow-ups

    • Returning duplicates when the same word can be formed along multiple paths or appears multiple times in the dictionary.
    • Forgetting to restore a board cell after recursion, which corrupts sibling DFS branches.
    • Follow-up: after a word is found, you can prune empty trie branches upward so future searches stop even earlier.

FAQ