79 单词搜索(dfs)

1. 问题描述:

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

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

示例:

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

给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false

提示:

board 和 word 中只包含大写和小写英文字母。
1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-search

2. 思路分析:

① 这道题目是经典的路径尝试问题,与之前的从左上角到右下角走迷宫的问题一样这种尝试可能性的问题最好的办法是使用dfs来解决的,我们需要先遍历二维数组board找到给出的单词中的第一个字母,因为只有当第一个字母符合要求的时候递归往下才有意义,找到单词中的第一个字母之后进行递归,在二维数组中从四个方向看一下能否找到满足条件的单词,只要是找到一个那么就直接返回true了,由于是上下左右四个方向所以对应着四个平行状态,对于这种经典的走路径或者是迷宫问题我们可以声明一个二维数组或者是两个一位数组来表示四个方向,这样就可以在for循环中对四个方向进行递归,一开始的时候只是尝试以单词中的第一个字母往下进行递归到递归出口的时候判断长度是否相等,并且两个字符串是否相等,这个是没有经过任何的优化的,所以会尝试所有与单词长度相等的单词等到最后才判断两个字符串是否相等,这样提交上去肯定是超时了,因为在递归的时候会搜索一些并不符合的答案,而这些答案在搜索的过程中就出现了不符合的字母所以完全不必要搜索下去了

② 于是我们可以对其进行优化,在往下递归之前先判断当前得到的记录字符串是否是给出单词的前缀,假如不是那么不用递归下去了,否则才递归下去,而且在添加当前的字符的时候记录字符串的长度需要小于给出的单词长度,这样剪枝之后可以减少一些不必要的搜索,经过优化之后提交上去就通过了,我们可以先将这些递归的代码写出来再结合具体的情况进行优化

下面是判断递归前判断已经形成的记录字符串是否是给出单词的前缀:

③ 我们还可以对其进行优化,我们可以在上面的代码中多传入一个参数用来记录递归的下一个状态中给出单词的字母是什么,这其实也好理解,比如给出的单词是SEE,第一个字母为S,往下进行递归所以下一个字母为E,在递归的时候从上下左右四个方向搜索是E的字母,假如发现有E的字母才递归下去,假如不是E说明肯定不符合要求了,这个优化比上面的方法好一点更加减少了不必要的搜索,同时当这个参数到达了给出单词的字符串的长度的时候说明二维数组中存在了这个单词返回true即可,只需要修改上面的代码一点点就可以完成(增加一个表示下一个递归状态中对于给出单词字母的位置,递归出口只需要判断出位置到达了给出单词的长度即可)

④ 因为图中对应四个方向的搜索所以我们在搜索的时候需要对之前遍历过的地方进行标记,因为每一个位置都会向四个方向搜索,假如不标记的话会造成重复搜索一个位置造成栈溢出了,这里可以使用一个二维数组来标记,并且在递归一开始的地方就对其标记即可,并且我们在递归调用结束之后进行回溯,因为尝试完了当前的状态回溯之后将之前的状态清楚就可以尝试下一个状态了

⑤ 对于这种走路径的代码其实不难理解,我们一个很直白的思路是我在当前的位置下往四个方向走,对于每一个位置都是一样的所以需要使用递归来解决,其中的关键是要把握其中的细节,递归的整体的代码框架都是类似的,特别是对于四个方向的搜索更是差不多的,直接在for循环中进行递归并且处理一些边界、递归出口与优化即可

3. 代码如下:

优化一:

class Solution {
    int rec[][];
    public boolean exist(char[][] board, String word) {
        int r = board.length;
        int c = board[0].length;
        rec = new int[r][c];
        char start = word.charAt(0);
        for (int i = 0; i < r; ++i){
            for (int j = 0; j < c; ++j){
                if (board[i][j] == start){
                    boolean res = dfs(r, c, board, word, i, j, start + "");
                    if (res) return true;
                    rec[i][j] = 0;
                }
            }
        }
        return false;
    }

    int dx[] = {-1, 1, 0, 0};
    int dy[] = {0, 0, -1, 1};
    private boolean dfs(int row, int col, char[][] board, String word, int r, int c, String cur) {
        if (word.equals(cur)) return true;
        rec[r][c] = 1;
        for (int i = 0; i < 4; ++i){
            int x = r + dx[i];
            int y = c + dy[i];
            String t = word.substring(0, cur.length());
            if (x >= 0 && x < row && y >= 0 && y < col && rec[x][y] == 0 && t.equals(cur) && cur.length() < word.length()){
                boolean f = dfs(row, col, board, word, x, y, cur + board[x][y]);
                rec[x][y] = 0;
                if (f){
                    return true;
                }
            }
        }
        return false;
    }
}

优化二:

class Solution {
    int rec[][];
    public boolean exist(char[][] board, String word) {
        int r = board.length;
        int c = board[0].length;
        rec = new int[r][c];
        char start = word.charAt(0);
        for (int i = 0; i < r; ++i){
            for (int j = 0; j < c; ++j){
                if (board[i][j] == start){
                    /*最后一个参数是用来优化的表示的*/
                    boolean res = dfs(r, c, board, word, i, j, start + "", 1);
                    if (res) return true;
                    rec[i][j] = 0;
                }
            }
        }
        return false;
    }

    /*除了使用给出的第一种方法之外还可以对其进行优化*/
    int dx[] = {-1, 1, 0, 0};
    int dy[] = {0, 0, -1, 1};
    private boolean dfs(int row, int col, char[][] board, String word, int r, int c, String cur, int pos) {
        if (pos == word.length()) return true;
        rec[r][c] = 1;
        for (int i = 0; i < 4; ++i){
            int x = r + dx[i];
            int y = c + dy[i];
            /*对于字符是一样的话才决定是否递归下去这也是优化的一个点*/
            char curChar = word.charAt(pos);
            if (x >= 0 && x < row && y >= 0 && y < col && rec[x][y] == 0 && board[x][y] == curChar && cur.length() < word.length()){
                boolean f = dfs(row, col, board, word, x, y, cur + board[x][y], pos + 1);
                rec[x][y] = 0;
                /*这里判断存在单词的时候直接返回true*/
                if (f){
                    return true;
                }
            }
        }
        return false;
    }
}

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值