Word Search

MediumBacktracking · Matrix DFS🧩 BacktrackingLeetCode #79 ↗Updated

Problem

You get an m x n grid of letters board and a string word. Decide whether word can be spelled by a path of cells where each step moves up, down, left or right. A single cell may be used at most once in the path.
Inputboard = [[A,B,C,E],[S,F,C,S],[A,D,E,E]], word = "ABCCED"Outputtrue
Inputsame board, word = "SEE"Outputtrue
Inputsame board, word = "ABCB"Outputfalse

1 ≤ m, n ≤ 6  ·  1 ≤ word.length ≤ 15  ·  upper- and lower-case English letters  ·  Follow-up: prune the search for larger boards.

Try it yourself

Java
class Solution {
    public boolean exist(char[][] board, String word) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Try every cell as a start and grow the path with DFS, marking a cell while it is on the path and unmarking it on the way back. Cheap letter-count checks can rule out hopeless searches before they start.
PatternBacktracking

Instinct Return ALL combinations, permutations, partitions or placements - or ask if any arrangement works - build a candidate step by step and undo each choice.

Memory hack Choose, explore, un-choose - walk the decision tree and snip a branch the moment it can no longer succeed.

Open the Backtracking guide →

Approaches

Brute ForceDFS with a Copied Visited Set

Time O(m · n · 3L · L)Space O(L2)

From each cell, explore all four neighbours for the next letter. To avoid reusing a cell, every call receives its own copy of the set of cells already on the path, so nothing ever has to be undone. Correct, but each step pays O(L) just to copy the set.

Java
public boolean exist(char[][] board, String word) {
    for (int r = 0; r < board.length; r++) {
        for (int c = 0; c < board[0].length; c++) {
            if (dfs(board, word, r, c, 0, new HashSet<>())) return true;
        }
    }
    return false;
}

private boolean dfs(char[][] b, String w, int r, int c, int i, Set<Integer> seen) {
    if (i == w.length()) return true;
    if (r < 0 || c < 0 || r >= b.length || c >= b[0].length) return false;
    int key = r * b[0].length + c;
    if (seen.contains(key) || b[r][c] != w.charAt(i)) return false;
    Set<Integer> path = new HashSet<>(seen); // private copy for this branch
    path.add(key);
    return dfs(b, w, r + 1, c, i + 1, path) || dfs(b, w, r - 1, c, i + 1, path)
        || dfs(b, w, r, c + 1, i + 1, path) || dfs(b, w, r, c - 1, i + 1, path);
}

No undo logic to get wrong, but the per-step set copies are pure overhead.

At a glance

LevelApproachTimeSpace
Brute ForceDFS with a Copied Visited SetO(m · n · 3L · L)O(L2)
BetterBacktracking with a Visited GridO(m · n · 3L)O(m · n)
OptimalIn-Place Marking + Letter-Count PruningO(m · n · 3L)O(L)
Common pitfalls
  • Not unmarking a cell on backtrack — later branches think it is still in use.
  • Checking the bounds after indexing board[r][c] — index out of range.
  • Returning early on success before restoring the cell in the in-place version.
Interview tips
  • State the search space: every start cell times at most 4 · 3L-1 paths.
  • Offer in-place marking as the memory upgrade, and ask if mutating the input is allowed.
  • Bring up the frequency check / reverse trick as the follow-up pruning answer.
Pattern
Grid DFS with backtracking → find a path of adjacent cells where cells cannot repeat.
My notes saved in this browser