We are given a 2D grid of characters and a target word. We need to determine if we can trace out the word by moving through adjacent cells (up, down, left, right), using each cell at most once per path.
This is a path-finding problem on a grid. We are looking for a specific sequence of characters, and at each step we can only move to a neighboring cell. The no-reuse constraint means we have to track which cells are currently part of the path, since the same cell could be a valid neighbor on a different path but not twice on the same one.
That structure points to backtracking. We build the word one character at a time, exploring the four directions from the current cell. When a branch hits a dead end (no neighbor matches the next character), we undo the last step and try a different direction. The small grid (at most 6x6) keeps the exponential search tractable, so backtracking is the practical choice here.
1 <= m, n <= 6 → The grid has at most 36 cells. An exponential backtracking search is affordable at this size, which rules out the need for a more elaborate algorithm.1 <= word.length <= 15 → Since each cell is used at most once per path, any matching path has length at most 36, so a 15-character word is always within reach if the characters line up.board and word consist of only lowercase and uppercase English letters → The matching is case-sensitive, so 'a' and 'A' are different characters.Generate every possible path whose length equals the word's length, starting from every cell, and check whether any of those paths spells out the target word. At each cell we branch into all four directions, appending characters to a running string. When the string reaches the word's length, we compare it against the word.
This is DFS without early termination. We never check whether the current partial path is still a valid prefix of the word, so we build out full paths and only compare at the end. It produces the correct answer but explores many paths that diverged from the word at the very first character.
The next approach prunes the search by checking each character as it goes, abandoning a branch the moment a cell fails to match the expected character.
Instead of building a full path and comparing at the end, check character by character as the DFS descends. At each step, verify that the current cell holds the expected character of the word at the current position. If it does not match, return false and stop exploring that branch.
That single check prunes the search tree. When board[i][j] does not equal word[index], none of the four directions from this cell can lead to a match, so they are never explored. The search now only follows paths that are valid prefixes of the word, which is far fewer than the blind enumeration of Approach 1.
For visited tracking, instead of a separate boolean matrix, we modify the board in place. When we enter a cell, we save its character and overwrite it with a sentinel marker ('#'). The recursive calls treat that marked cell as a non-match (it equals no letter of the word), so the path cannot revisit it. When the call returns, we restore the original character.
The in-place marking is safe because the board contains only English letters, so the sentinel '#' never equals a real word character. A marked cell therefore fails the board[i][j] != word[index] check on any recursive call, which is what prevents a path from reusing it. Restoring the character after the recursive calls return means the board is unchanged once exist finishes, so a starting cell that failed does not corrupt the board for the next starting cell.
The || chain short-circuits: the first direction that returns true stops the remaining directions from running, so a found word returns up the stack immediately.
board[i][j] matches word[0]. If so, start a DFS from that cell.board[i][j] does not match word[index], return false.'#'.index + 1.This is the standard optimal solution. The next approach keeps the same backtracking core but adds cheap preprocessing that rejects impossible inputs up front and reduces the number of cells the DFS starts from.
The backtracking from Approach 2 already has the best worst-case complexity available for this problem. The improvements here do not change that bound, but they cut the average-case work with two pieces of preprocessing.
The first is a frequency check. Count how many times each character appears in the board, then walk the word and tally its characters. If the word needs more of any character than the board contains (it needs five 'A's but the board has three), no path can spell it, so we return false before running any DFS.
The second concerns which end of the word to search from. A path that spells word forwards from a starting cell also spells the reversed word backwards from the same path's end. So searching for the reversed word gives the same answer, but the DFS now starts only from cells matching the word's last character. If the last character is rarer in the board than the first, reversing reduces the number of starting cells, and fewer starting cells means fewer top-level DFS calls.
Adjacency in the grid is symmetric: if cell A is a neighbor of cell B, then B is a neighbor of A. So a sequence of cells that spells word reading forward spells reverse(word) reading backward, over the exact same cells. The set of valid paths is identical; only the direction we walk them changes. Reversing the word therefore cannot change the true/false answer.
What it changes is the starting set. The DFS launches from every cell matching the search word's first character. Consider a board that is mostly 'A' with a word that starts with 'A' and ends with 'Z'. Searching forward starts the DFS from nearly every cell; searching the reversed word starts only from the few 'Z' cells. The worst-case bound is unchanged, but fewer top-level calls means less work on inputs like this.
word[0] versus word[word.length - 1]. If the first character has more matches, reverse the word to start the search from the rarer end.