function exist(board, word) {const R = board.length, C = board[0].length;
function dfs(r, c, i) {if (i === word.length) return true;
if (r<0||c<0||r>=R||c>=C) return false;
if (board[r][c] !== word[i]) return false;
const tmp = board[r][c];
board[r][c] = '#'; // mark used
const found = dfs(r+1,c,i+1) || dfs(r-1,c,i+1)
|| dfs(r,c+1,i+1) || dfs(r,c-1,i+1);
board[r][c] = tmp; // backtrack
return found;
}
for (let r = 0; r < R; r++)◄
for (let c = 0; c < C; c++)
if (dfs(r, c, 0)) return true;
return false;
}
Any cell could be the first letter, so try starting a depth-first search from every cell.
From a cell, the DFS matches word[i] here, then recurses into the four neighbours looking for word[i+1] — a classic “try a choice, recurse, undo” backtracking shape.
To stop a path from reusing a cell, temporarily overwrite it (e.g. with “#”) before recursing, then restore it afterwards. That restore is the backtrack.
A branch dies on three conditions: you run off the grid, the letter does not match, or the cell is already in the current path. If any neighbour reaches the end of the word, the answer is true.