Skip to content

Word Search II

Given an m × n board of characters and a list of strings words, return all words on the board.

Each word must 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.

BoardWordsOutput
[["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]["oath","pea","eat","rain"]["eat","oath"]
[["a","b"],["c","d"]]["abcb"][]
  • m == board.length, n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] is a lowercase English letter.
  • 1 <= words.length <= 3 × 10^4
  • 1 <= words[i].length <= 10
ApproachTimeSpace
Recursive DFSO(n × m × 4^l)O(w × l)
Iterative BacktrackO(n × m × 4^l)O(w × l)

n,m = board dims, l = max word len, w = word count

  • Pick Recursive DFS for simplicity — natural for tree/graph traversal.
  • Pick Iterative Backtrack if you prefer explicit state management.
Natural Fit
Recursive DFS
O(n×m×4^l) time · O(w×l) space
Explicit State
Backtrack with Visited Set
O(n×m×4^l) time · O(w×l) space
★ Recommended
  1. Build a Trie from all words.
  2. For each cell on the board, start a DFS.
  3. Traverse the board following the Trie structure.
  4. Mark visited cells by temporarily changing the board.
  5. Restore the board during backtracking.
⏱ Time O(n×m×4^l) Board × word length 💾 Space O(w×l) Trie size
function findWords(board, words):
root = buildTrie(words)
result = []
for i, j in board:
dfs(board, i, j, root, result)
return result
function dfs(board, i, j, node, result):
char = board[i][j]
if char not in node.children:
return
nextNode = node.children[char]
if nextNode.word exists:
result.add(nextNode.word)
nextNode.word = null // Avoid duplicates
board[i][j] = '#' // Mark visited
for each neighbor (ni, nj):
dfs(board, ni, nj, nextNode, result)
board[i][j] = char // Restore
word_search_ii_recursive.py
from typing import List
class TrieNode:
def __init__(self):
self.children = {}
self.word = None
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
root = TrieNode()
for word in words:
node = root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.word = word
result = []
for i in range(len(board)):
for j in range(len(board[0])):
self.dfs(board, i, j, root, result)
return result
def dfs(self, board, i, j, node, result):
char = board[i][j]
if char not in node.children:
return
next_node = node.children[char]
if next_node.word is not None:
result.append(next_node.word)
next_node.word = None
board[i][j] = '#'
for di, dj in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
ni, nj = i + di, j + dj
if 0 <= ni < len(board) and 0 <= nj < len(board[0]):
self.dfs(board, ni, nj, next_node, result)
board[i][j] = char
sol = Solution()
board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]]
words = ["oath", "pea", "eat", "rain"]
print(sol.findWords(board, words)) # ['eat', 'oath']
🎯 Interview Favourite

Instead of modifying the board, track visited cells in a separate set. Useful when board is read-only.

⏱ Time O(n×m×4^l) Board exploration 💾 Space O(n×m+w×l) Visited set + Trie
word_search_ii_backtrack.py
from typing import List
class TrieNode:
def __init__(self):
self.children = {}
self.word = None
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
root = TrieNode()
for word in words:
node = root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.word = word
self.result = []
visited = set()
def backtrack(i, j, node):
if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]):
return
if (i, j) in visited:
return
char = board[i][j]
if char not in node.children:
return
visited.add((i, j))
next_node = node.children[char]
if next_node.word is not None:
self.result.append(next_node.word)
next_node.word = None
for di, dj in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
backtrack(i + di, j + dj, next_node)
visited.remove((i, j))
for i in range(len(board)):
for j in range(len(board[0])):
backtrack(i, j, root)
return self.result
sol = Solution()
board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]]
words = ["oath", "pea", "eat", "rain"]
print(sol.findWords(board, words)) # ['eat', 'oath']
✓ 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_ii_space_optimized.py
"""
Solution for Word Search II
"""
def solve():
"""Implementation for approach 3 of Word Search II"""
pass
if __name__ == "__main__":
print("Solution ready")