LeetCode 👉 HOT 100 👉 单词搜索 - 中等题

84 阅读3分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

题目

给定一个 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

思路

分析题意,可以得出一些简单的理论,board 中的任何一个位置,都可能作为 word 的起点,向 上、下、左、右 四个方向前进,而每次前进的方向,不能在 走过的路径 上。

如果假设,从坐标 [x, y] 开始,能够搜索到单词 word,那么必然有这样一个结论:

`[x, y]` 作为起点,`board[x][y]` 必然与 `word` 的首字符 `word.charAt(0)` 相等

同时,`[x, y]` 的 `上、下、左、右` 四个方向,必然也存在某个位置(假设为 [dx, dy]),`board[dx][dy]` 与 `word.charAt(1)` 相等

...

依次类推,可以得出一个校验函数 check(x, y, idx),表示从 [x, y] 开始,能否搜索到 word[idx, idx + 1, ...]

利用这个函数,我们以 board 的每个位置作为起点,分别校验 check(x, y, 0),如果存在一个最终结果为 true,则说明搜索成功;如果所有的校验都完成后,依然没有搜索到完整的 word,则说明 board 不存在 word;

DFS完整代码

    
    /**
     * @param {character[][]} board
     * @param {string} word
     * @return {boolean}
     */
    var exist = function (board, word) {

        let m = board.length, n = board[0].length;

        // 定义一个访问数组,规格 和 board 一样
        const visited = new Array(m).fill(0).map(item => new Array(n).fill(false));

        // 四个方向
        const directions = [[0, -1], [0, 1], [1, 0], [-1, 0]]

        // 检查 从 board[x][y] 开始,能否搜索到 word[k, k + 1, ...] 之后的子串
        const check = (x, y, idx) => {

            // 如果当前位置不匹配,说明搜索失败
            if(board[x][y] != word.charAt(idx)) {
                return false;
            } else if(idx == word.length - 1) {
                // 如果当前配置匹配成功,且匹配进行到了最后一个位置,说明搜索成功
                // board[x][y] == word.charAt(word.length - 1)

                return true;
            }
            // 在本次循环中,将当前位置标记为 已访问
            visited[x][y] = true;
            let result = false;
            for(let [dx, dy] of directions) {
                // 获取新的位置
                let newi = x + dx, newy = y + dy;

                // 新位置不能越界
                if(newi >= 0 && newi < m && newy >= 0 && newy < n) {
                    // 不能走已访问的位置
                    if(!visited[newi][newy]){
                        // 递归再次校验下一个字符
                        let flag = check(newi, newy, idx + 1);

                        // 这里如果返回为true,认为存在某个方向,可继续进行搜索
                        if(flag) {
                            result = true;
                            break;
                        }
                    }
                }
            }
            // 需要在使用完毕后,将当前 x,y 的已访问标记删除
            visited[x][y] = false;
            return result;
        }

        for(let i = 0; i < m; i++) {
            for(let j = 0; j < n; j++) {
                // 从起点开始,搜索 word
                let flag = check(i, j, 0);

                // 这里如果为 true, 说明从0开始,可以一直搜索到 word 的尾部
                // 搜索成功
                if(flag) {
                    return true;
                }
            }
        }
        
        // 最后没有搜索成功的话,默认搜索失败
        return false;
    };

小结

DFS 回溯算法,往往时间复杂度会很高,要注意不必要的递归,进行剪枝

# LeetCode 👉 HOT 100 👉 单词搜索 - 中等题

合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍

如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄