「前端刷题」79. 单词搜索

177 阅读1分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」。

题目

链接:leetcode-cn.com/problems/wo…

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

 

提示:

  • m == board.length
  • n = board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • boardword 仅由大小写英文字母组成

 

**进阶:**你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?

解题思路

思路1

回溯算法(深度优先搜索)

  1. 遍历矩阵board 在每次遍历以当前元素为起点进行深度优先搜索
  2. 分别遍历上下左右四个方向,匹配到word即返回true
    关键
  • 深度优先继续条件:
    • 当前元素不超过矩阵边界: newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length
    • 当前元素在此次搜索中没有访问过:  维护矩阵visited记录元素是否访问过
  • 深度优先结束标志:
    • 当前元素不等于要匹配的字符 -> 返回false
    • 否则,如果当前访问过的字符串长度已经等于word长度 -> 返回true

代码

var exist = function(board, word) {
    const visited = new Array(board.length);
    for (let i = 0; i < board.length; i++) {
        visited[i] = new Array(board[0].length).fill(false);
    }
    for (let i = 0; i < board.length; i++) {
        for (let j = 0; j < board[0].length; j++) {
            if (dfs(board, word, visited, i, j, 0)) return true;
        }
    }
    return false;
};
const dfs = (board, word, visited, i, j, k) => { 
    // ! locate element in char array
    if (board[i][j] !== word.charAt(k)) return false;
    else if (k === word.length - 1) return true;
    visited[i][j] = true;
    const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
    let res = false;
    for (const [dx, dy] of directions) {
        const newi = i + dx, newj = j + dy;
        if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
            if (!visited[newi][newj]) {
                if (dfs(board, word, visited, newi, newj, k + 1)) {
                    res = true;
                    break;
                }
            }
        }
    }
    visited[i][j] = false;
    return res;
}

思路2

  1. 首先遍历board,从每一个位置开始使用回溯,如果从当前位置出发找到了找到了就结束返回true,当前位置没有找到就下一个,如果都没有找到就返回false.
  2. 递归方法,我是按照上右下左的顺序开始的,大家可以自定义,结束条件是index值等于word.length-1,表示前面的全部匹配上了。
  3. 注意每一次查找都需要判断是否全部匹配,如果全部匹配可以直接返回,如果不匹配再接着上右下左这个顺序查找。
    image.png
    image-20210530164438344.png

代码

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
    var m = board.length
    var n = board[0].length
        //初始化一个访问数组,visited,false;
    let visited = new Array(m).fill(false).map(() => new Array(n).fill(false));
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            //0表示匹配word字符的位置;
            let flag = huis(i, j, word, 0);
            if (flag) {
                return flag;
            }
        }
    }

    function huis(i, j, word, index) {
        // 按照上右下左的顺序来查询字符串
        if (i >= 0 && i <= m - 1 && j >= 0 && j <= n - 1 && board[i][j] == word[index] && visited[i][j] == false) {
            if (index == word.length - 1) {
                return true;
            }
            let flag = false;
            visited[i][j] = true;
            flag = huis(i - 1, j, word, index + 1);
            if (flag) {
                return flag;
            }
            flag = huis(i, j + 1, word, index + 1);
            if (flag) {
                return flag;
            }
            flag = huis(i + 1, j, word, index + 1);
            if (flag) {
                return flag;
            }
            flag = huis(i, j - 1, word, index + 1);
            if (flag) {
                return flag;
            }
            visited[i][j] = false;
            return false;
        } else {
            return false;
        }
    }
    return false;
};