单词搜索(DFS + 回溯)

LeetCode 79 题解笔记

Posted by BY on April 26, 2026

原始笔记已经记下了”DFS + 回溯算法”和”main 循环结束后也要回溯 flag”的关键点。这里在不改代码的前提下,把题意和标准的回溯模板写完整。

题目背景

LeetCode 79「单词搜索」。给一个 m × n 的字符网格 board 和一个目标单词 word,判断能否在网格中按水平/垂直相邻的方式拼出 word。同一格子在一次拼接中不能重复使用

概念解释

  • DFS(深度优先搜索):沿着一条路径深入,走不通再回退尝试别的方向。
  • 回溯(backtracking):DFS 中”走过又退回”时,必须把状态(这里是”该格是否已被本次路径占用”的 flag 标记)复原,否则会污染兄弟分支。
  • 方向数组:网格四邻域问题常用 (±1, 0)/(0, ±1) 四个偏移;本笔记把四个方向直接展开成四段 if,逻辑等价。

实现原理

  1. exist 中双重循环枚举每个起点 (i, j),若 board[i][j] == word[0],则把该格 flag 置 1,调用 dfs(...,1,ended) 尝试匹配后续字符。
  2. dfs(arr, flag, i, j, word, k, ended) 的语义是”已经匹配了 word[0..k-1],当前停在 (i, j)“,目标是匹配 word[k]
    • k == word.size() 即已全匹配,置 ended = true 返回;
    • 否则尝试 (i, j-1) / (i-1, j) / (i+1, j) / (i, j+1) 四个邻居,命中且未被占用就标记 flag 后递归,递归返回后立刻把 flag 清回 0,这就是回溯。
  3. 起点自身的 flag 也必须在 dfs 返回后清 0(无论是否命中),否则后续起点会受影响——这正是原文提示”main 函数 for 循环完成后也需要回溯 flag”的原因。
  4. 一旦 ended 为真就直接返回 true,提前结束所有搜索。

最坏时间复杂度 O(m·n·4^L),其中 L = word.size();空间复杂度 O(L) 递归栈 + O(m·n)flag 数组。

参考实现

解题思路: dfs + 回溯算法   
需要注意的点:main函数内for循环完成后,也需要回溯flag的值    
class Solution {

private:
void dfs(const std::vector<std::vector<char>>& arr, std::vector<std::vector<int>>& flag, int i, int j, const std::string& word,int k, bool& ended)
{
	// std::cout << "i = " << i << " j = " << j << " k = " << k << std::endl;
	if (k == word.size())
	{
		ended = true;
		return;
	}

	if (i > -1 && i < arr.size() && (j - 1) > -1 && (j - 1) < arr[0].size())
	{
		//std::cout << "i = " << i << " j - 1 " << j - 1 << std::endl;
		if (!flag[i][j - 1] && arr[i][j - 1] == word[k])
		{
			flag[i][j - 1] = 1;
			dfs(arr,flag,i,j-1,word,k+1, ended);
			flag[i][j - 1] = 0;
		}
	}

	if ( (i-1) > -1 && (i-1) < arr.size() && j > -1 && j < arr[0].size())
	{
		//std::cout << "i-1 = " << i-1 << " j " << j << std::endl;
		if (!flag[i-1][j] && arr[i-1][j] == word[k])
		{
			flag[i-1][j] = 1;
			dfs(arr, flag, i-1, j , word, k + 1, ended);
			flag[i-1][j] = 0;
		}
	}

	if (i+1 > -1 && i+1 < arr.size() && j  > -1 && j < arr[0].size())
	{
		//std::cout << "i + 1 = " << i + 1 << " j  " << j << std::endl;
		if (!flag[i+1][j] && arr[i+1][j] == word[k])
		{
			flag[i+1][j] = 1;
			dfs(arr, flag, i+1, j, word, k + 1, ended);
			flag[i+1][j] = 0;
		}
	}

	if (i > -1 && i < arr.size() && j + 1 > -1 && j + 1 < arr[0].size())
	{
		//std::cout << "i = " << i << " j + 1 " << j + 1 << std::endl;
		if (!flag[i][j + 1] && arr[i][j + 1] == word[k])
		{
			flag[i][j + 1] = 1;
			dfs(arr, flag, i, j + 1, word, k + 1, ended);
			flag[i][j + 1] = 0;
		}
	}

	//return false;
}
public:
    bool exist(vector<vector<char>>& board, string word) {

	std::vector<std::vector<int>> flag(board.size(), std::vector<int>(board[0].size(),0));

	bool ended = false;
	for (int i = 0; i < board.size(); ++i)
	{
		for (int j = 0; j < board[0].size(); ++j)
		{
			ended = false;
			if (board[i][j] == word[0])
			{
				flag[i][j] = 1;
				dfs(board,flag,i,j,word,1,ended);
                flag[i][j] = 0;
				if (ended)
				{
					std::cout << ended << std::endl;
					return true;
				}

			}
		}
	}
    return false;
	// std::cout << ended << std::endl;
    }
};