Trace a word through a letter grid

Backtracking Mid-level Python 3.13+ Reviewed By Best Latest Apps

Pick the fix

Your search finds 'ABA' in a 1 × 2 grid 'AB'. What is wrong?

Options

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A A cell is reused; mark cells as used while they are on the current path

Right: 'ABA' would need the first A twice.

The used mark must last exactly as long as the cell is on the current path, which is why it is undone on the way back.

Why not the others?
  • B The grid is too small to search. Small grids are searched the same way.
  • C Diagonal moves are missing. Diagonals are not allowed and would not help here.

30-second answer

Start a depth-first search from every cell that matches the first letter. At each step, mark the cell used, try the four neighbours for the next letter, then unmark it. A branch stops as soon as a letter does not match. Worst case O(R · C · 3^L) for a word of length L, since each step has at most three unused directions. The recursion and the used set take O(L) space.

Practise saying it · 60 s. No recording, just a timer

Explain tracing a word through a grid.

Say your answer out loud, then check which of these points you covered:

  • DFS from matching cells
  • Mark, recurse, unmark
  • O(R · C · 3^L)

Deep dive

Mark and unmark on the way in and out: that is the backtracking part.

Quick rejects: the word is longer than the grid, or the grid lacks enough of some letter.

Searching from the rarer end of the word can cut the work a lot.

Python solution Ran on Python 3.13.5 · mypy --strict
Python 3.13
STEPS = ((1, 0), (-1, 0), (0, 1), (0, -1))


def can_trace(grid: list[str], word: str) -> bool:
    used: set[tuple[int, int]] = set()

    def follow(y: int, x: int, done: int) -> bool:
        if done == len(word):  # every letter matched
            return True
        if not (0 <= y < len(grid) and 0 <= x < len(grid[0])):
            return False
        if (y, x) in used or grid[y][x] != word[done]:
            return False
        used.add((y, x))  # on the current path
        found = any(follow(y + dy, x + dx, done + 1)
                    for dy, dx in STEPS)
        used.discard((y, x))  # free it for other paths
        return found

    return any(follow(y, x, 0) for y, row in enumerate(grid)
               for x in range(len(row)))

Common mistakes

  • Forgetting to unmark, which blocks cells for other paths.
  • Allowing a cell to be reused.

Senior take & follow-ups

Mention the letter-count reject; it shows practical sense.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you search for many words at once?

    Show hint

    Put the words in a trie and walk the trie alongside the grid search.

  2. Why 3^L and not 4^L?

    Show hint

    After the first step, the cell you came from is already used.

What each operation costs: Depth First Search

From the author

DSA Master - Data & Algorithms

Free on iPhone and iPad: watch 50+ data structures and algorithms step through animations, run the code in the playground, and try the daily challenge.

Suggest a fix if something here is wrong or out of date.