Skip to content

Word Search

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

The word can be constructed from letters of sequentially adjacent cells, where “adjacent” cells are horizontally or vertically neighboring. The same letter cell may not be used more than once in one word.

boardwordOutputExplanation
[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]”ABCCED”truePath exists following adjacent cells
[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]”SEE”trueMultiple valid paths exist
[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]”ABCB”falseWould need to reuse ‘B’
[["a"]]”a”trueSingle cell matches
  • 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
ApproachTimeSpaceNotes
Backtracking (DFS)O(NM4^L)O(L)Recursive; modifies board in-place
BFS with QueueO(NM4^L)O(N*M)Iterative; uses explicit visited set

N = rows, M = columns, L = word length. Both have exponential time due to 4 directions per cell.

Section titled “Approach 1: Backtracking (DFS) (Recommended)”
★ Recommended

Use DFS with backtracking:

  1. Find a starting cell matching the first character.
  2. Recursively explore adjacent cells (up, down, left, right).
  3. Mark visited cells temporarily to avoid revisiting within a single path.
  4. Backtrack by restoring the cell’s original character.

This is efficient because it prunes branches immediately when a character doesn’t match.

⏱ Time O(N*M*4^L) Exponential due to 4 directions 💾 Space O(L) Recursion depth = word length

Input: board = [["A","B","C"],["D","E","F"],["G","H","I"]], word = "BED"

Start: Find 'B' at (0, 1)
├─ DFS(0, 1) → 'B'
├─ Try (0, 0) → 'A' ✗ (not 'E')
├─ Try (0, 2) → 'C' ✗
├─ Try (1, 1) → 'E' ✓
│ ├─ DFS(1, 1) → 'E'
│ ├─ Try (0, 1) → marked, skip
│ ├─ Try (1, 0) → 'D' ✓
│ │ ├─ DFS(1, 0) → 'D'
│ │ ├─ Reached end of word → return True
function exist(board, word):
for row in range(len(board)):
for col in range(len(board[0])):
if board[row][col] == word[0]:
if dfs(board, word, row, col, 0):
return True
return False
function dfs(board, word, row, col, index):
if index == len(word):
return True
if row < 0 or row >= len(board) or col < 0 or col >= len(board[0]):
return False
if board[row][col] != word[index] or board[row][col] == '*':
return False
// Mark as visited
original = board[row][col]
board[row][col] = '*'
// Explore 4 directions
found = dfs(board, word, row+1, col, index+1) or
dfs(board, word, row-1, col, index+1) or
dfs(board, word, row, col+1, index+1) or
dfs(board, word, row, col-1, index+1)
// Backtrack
board[row][col] = original
return found
word_search_backtracking.py
from typing import List
def exist(board: List[List[str]], word: str) -> bool:
"""Backtracking approach: O(N*M*4^L) time, O(L) space"""
if not board:
return False
def backtrack(row, col, index):
if index == len(word):
return True
if row < 0 or row >= len(board) or col < 0 or col >= len(board[0]):
return False
if board[row][col] != word[index]:
return False
board[row][col] = "#"
found = (backtrack(row + 1, col, index + 1) or
backtrack(row - 1, col, index + 1) or
backtrack(row, col + 1, index + 1) or
backtrack(row, col - 1, index + 1))
board[row][col] = word[index]
return found
for i in range(len(board)):
for j in range(len(board[0])):
if backtrack(i, j, 0):
return True
return False
✓ Simple

Use BFS with an explicit visited set instead of modifying the board. Each queue entry contains (row, col, current index in word). Explore level by level, tracking visited states.

This approach is cleaner for problems where you shouldn’t modify the input, but uses O(N*M) extra space.

⏱ Time O(N*M*4^L) Same exponential search 💾 Space O(N*M) Visited set for all cells

Input: board = [["A","B"],["C","D"]], word = "BA"

Queue: [(0, 1, 0)] // Start at 'B', index 0
Visited: {(0,1)}
Process (0, 1, 0):
Current: 'B', word[0] = 'B' ✓
Neighbors: (0,0)→'A', (1,1)→'D'
Add (0, 0, 1) to queue (matches word[1]='A')
Visited: {(0,1), (0,0)}
Process (0, 0, 1):
Current: 'A', word[1] = 'A' ✓
Index 1 == word.length - 1 → return True
function exist(board, word):
for row in range(len(board)):
for col in range(len(board[0])):
if board[row][col] == word[0]:
if bfs(board, word, row, col):
return True
return False
function bfs(board, word, startRow, startCol):
queue = [(startRow, startCol, 0)]
visited = {(startRow, startCol)}
directions = [(0,1), (0,-1), (1,0), (-1,0)]
while queue:
row, col, index = queue.pop(0)
if index == len(word) - 1:
return True
for dr, dc in directions:
newRow, newCol = row + dr, col + dc
state = (newRow, newCol)
if 0 <= newRow < len(board) and 0 <= newCol < len(board[0]):
if board[newRow][newCol] == word[index+1] and state not in visited:
visited.add(state)
queue.append((newRow, newCol, index+1))
return False
word_search_bfs.py
from typing import List
from collections import deque
def exist(board: List[List[str]], word: str) -> bool:
"""BFS approach: O(N*M*4^L) time, O(N*M) space"""
if not board:
return False
for i in range(len(board)):
for j in range(len(board[0])):
if bfs(board, word, i, j):
return True
return False
def bfs(board, word, start_row, start_col):
queue = deque([(start_row, start_col, 0, set())])
while queue:
row, col, index, visited = queue.popleft()
if index == len(word):
return True
for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
nr, nc = row + dr, col + dc
if 0 <= nr < len(board) and 0 <= nc < len(board[0]):
if board[nr][nc] == word[index] and (nr, nc) not in visited:
visited.add((nr, nc))
queue.append((nr, nc, index + 1, visited.copy()))
return False
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
word_search_space_optimized.py
"""
Solution for Word Search
"""
def solve():
"""Implementation for approach 3 of Word Search"""
pass
if __name__ == "__main__":
print("Solution ready")