150NeetCode 150

76.Word Search

MediumBlind 75Backtracking · 6/9

Problem

Given an m x n grid of characters board and a string word, return true if word exists in the grid.

The word must be built from letters of sequentially adjacent cells, where adjacent means horizontally or vertically neighbouring. The same cell may not be used more than once in a single word.

Examples

Input:  board = [["A","B","C","E"],
                 ["S","F","C","S"],
                 ["A","D","E","E"]], word = "ABCCED"
Output: true
Explanation: A(0,0) → B(0,1) → C(0,2) → C(1,2) → E(2,2) → D(2,1).
Input:  same board, word = "SEE"
Output: true
Input:  same board, word = "ABCB"
Output: false
Explanation: The only B is at (0,1) and it cannot be reused.

Constraints

  • m == board.length, n == board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • board and word consist of only lowercase and uppercase English letters.

💡 Key Insight

Try every cell as a starting point and run a DFS that matches one character per step, moving in 4 directions. A cell may appear only once on the current path, so mark it as visited while it is on the path and unmark it when backtracking — other paths may legitimately use it. Marking in place (overwrite with '#') avoids an extra visited array, and a quick character-count check can reject impossible words before any search.

Backtracking with a Visited Array

SpaceO(m · n) for visited plus O(L) recursion depth.

Intuition

From each cell whose letter equals word[0], recursively look for word[1] among the 4 neighbours, and so on. A separate boolean[][] visited keeps track of the cells on the current path.

Algorithm

  1. For each cell (r, c): if dfs(r, c, 0) returns true, return true.
  2. dfs(r, c, k):
    • If k == word.length(), return true (all characters matched).
    • If out of bounds, visited[r][c], or board[r][c] != word[k], return false.
    • Mark visited[r][c] = true.
    • Recurse into the 4 neighbours with k + 1; if any is true, the answer is true.
    • Unmark visited[r][c] = false and return the result.
  3. Return false.

Visualization

word = "ABCCED"

A B C E        step 0: A at (0,0)
S F C S        step 1: B at (0,1)
A D E E        step 2: C at (0,2)
               step 3: C at (1,2)  (going right to E(0,3) fails)
               step 4: E at (2,2)
               step 5: D at (2,1)  -> k == 6, found

Code

java
class Solution {
    private char[][] board;
    private String word;
    private boolean[][] visited;

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

    private boolean dfs(int r, int c, int k) {
        if (k == word.length()) return true; // every character matched
        if (r < 0 || c < 0 || r >= board.length || c >= board[0].length) return false;
        if (visited[r][c] || board[r][c] != word.charAt(k)) return false;

        visited[r][c] = true;
        boolean found = dfs(r + 1, c, k + 1) || dfs(r - 1, c, k + 1)
                || dfs(r, c + 1, k + 1) || dfs(r, c - 1, k + 1);
        visited[r][c] = false; // backtrack: free the cell for other paths
        return found;
    }
}

Complexity

  • Time: O(m · n · 4 · 3^(L-1)) — L = word.length(); from each start the first step has 4 directions and every later step at most 3 (it cannot go back).
  • Space: O(m · n) for visited plus O(L) recursion depth.

Complexity Comparison

Approach Time Space
Backtracking + visited[][] O(m · n · 3^L) O(m · n + L)
In-place Marking + Pruning O(m · n · 3^L) worst, pruned O(L)

Common Pitfalls

  • Not unmarking the cell after exploring it — a cell used by a failed branch becomes permanently blocked for later paths.
  • Checking k == word.length() after the bounds check — a word ending at the board edge then wrongly fails when the next call goes out of bounds.
  • Returning as soon as one direction fails instead of trying all four.
  • Forgetting to restore board[r][c] before returning true leaves the input corrupted (harmless on LeetCode, a bug elsewhere) — the code above restores before returning.