Word Search II – Solution & Complexity

Solution Walkthrough

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. 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. 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. 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. 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