【回溯-6】79.单词搜索

题目描述:

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中"相邻"单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

复制代码
输入:board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "ABCCED"
输出:true

示例 2:

复制代码
输入:board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "SEE"
输出:true

示例 3:

复制代码
输入:board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "ABCB"
输出:false

解题思路:

方法一:回溯 + DFS

核心思路:

从每个格子出发,尝试匹配 word 的第一个字符,然后向四个方向扩展。

  • 匹配成功:继续匹配下一个字符

  • 匹配失败:回溯,换方向

  • 越界或已访问:跳过

关键操作:

  1. 标记已访问 :把当前格子改成特殊字符(如 '#'),避免重复使用

  2. 四个方向:上、下、左、右

  3. 回溯:恢复格子的原字符

具体过程示例:

board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "ABCCED"

复制代码
路径: A → B → C → C → E → D
(0,0) → (0,1) → (0,2) → (1,2) → (2,2) → (2,1)

A B C E
S F C S
A D E E

代码实现:

cpp 复制代码
class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        int m = board.size(), n = board[0].size();
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(board, word, i, j, 0)) {
                    return true;
                }
            }
        }
        
        return false;
    }
    
private:
    bool dfs(vector<vector<char>>& board, string& word, int i, int j, int index) {
        // 匹配完成
        if (index == word.size()) return true;
        
        // 越界或不匹配
        if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size()
            || board[i][j] != word[index]) {
            return false;
        }
        
        // 标记已访问
        char temp = board[i][j];
        board[i][j] = '#';
        
        // 四个方向搜索
        bool found = dfs(board, word, i + 1, j, index + 1)
                  || dfs(board, word, i - 1, j, index + 1)
                  || dfs(board, word, i, j + 1, index + 1)
                  || dfs(board, word, i, j - 1, index + 1);
        
        // 恢复
        board[i][j] = temp;
        
        return found;
    }
};

复杂度分析:

设 m × n 是网格大小,L 是单词长度。

维度 复杂度 说明
时间复杂度 O(m × n × 3^L) 每个格子出发,每步最多3个方向(不回头)
空间复杂度 O(L) 递归栈深度

为什么是 3^L 而不是 4^L?

因为每次不能走回头路(已访问的格子被标记),所以每步最多3个方向。

关键细节:

1. 为什么用 '#' 标记已访问?
  • 避免重复使用同一个格子

  • 比额外维护 visited 数组更省空间

  • 回溯时恢复原字符

2. 为什么四个方向用 || 连接?

只要有一个方向能找到,就返回 true。|| 有短路特性,找到后不再继续搜索。

3. 为什么先判断 index == word.size()?

当 index 等于单词长度时,说明所有字符都匹配完了,返回 true。

4. 为什么越界判断放在前面?

避免访问越界的内存,同时判断字符是否匹配。

方法二:用 visited 数组

代码实现:

cpp 复制代码
class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        int m = board.size(), n = board[0].size();
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(board, word, visited, i, j, 0)) {
                    return true;
                }
            }
        }
        
        return false;
    }
    
private:
    bool dfs(vector<vector<char>>& board, string& word,
             vector<vector<bool>>& visited, int i, int j, int index) {
        if (index == word.size()) return true;
        
        if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size()
            || visited[i][j] || board[i][j] != word[index]) {
            return false;
        }
        
        visited[i][j] = true;
        
        bool found = dfs(board, word, visited, i + 1, j, index + 1)
                  || dfs(board, word, visited, i - 1, j, index + 1)
                  || dfs(board, word, visited, i, j + 1, index + 1)
                  || dfs(board, word, visited, i, j - 1, index + 1);
        
        visited[i][j] = false;
        
        return found;
    }
};

缺点:需要额外 O(m × n) 空间。

两种方法对比:

方法 时间复杂度 空间复杂度 推荐度
原地标记 O(m × n × 3^L) O(L) ⭐⭐⭐⭐⭐
visited 数组 O(m × n × 3^L) O(m × n + L) ⭐⭐⭐⭐

总结:

要点 说明
核心思想 从每个格子出发,DFS 匹配单词
关键操作 标记已访问 → 四方向搜索 → 恢复
终止条件 index == word.size() 返回 true
时间复杂度 O(m × n × 3^L)
空间复杂度 O(L)
相关推荐
ZhangJun951 小时前
Mobike 共享单车分析项目
人工智能·python·算法·kmeans·聚类·knn
Navigator_Z1 小时前
LeetCode //C - 1281. Subtract the Product and Sum of Digits of an Integer
c语言·算法·leetcode
薛定e的猫咪1 小时前
(AISTATS 2023)BaCaDI 逐章阅读
人工智能·深度学习·算法·机器学习
Benny_Tang1 小时前
AT_arc180_d [ARC180D] Division into 3 题解
数据结构·c++·算法
殷色玫瑰2 小时前
C++ STL:stack、queue、priority_queue 与容器适配器详解
java·开发语言·数据结构·c++·算法·visualstudio
努力努力再努力wz2 小时前
【CUDA入门系列】CUDA编程实战:设备选择、显存管理、Kernel、Stream 与 Event 全流程梳理
开发语言·c++
Evand J2 小时前
【电机滤波例程1】PMSM永磁同步电机转速与位置估计,MATLAB程序:标准无迹卡尔曼滤波、模型说明与仿真验证
算法·matlab·电机·ekf·负载·卡尔曼滤波
沫璃染墨2 小时前
《从零入门Linux系统篇(五十八):线程篇·十一——线程安全与死锁详解:从可重入到多锁管理》
linux·服务器·开发语言·c++·驱动开发·安全·架构
打码的老程是远篁2 小时前
手把手教你玩转大模型——3. Transformer 的位置编码——模型怎么知道谁在前,谁在后?
人工智能·算法