当前位置: 首页 > article >正文

【Leetcode 热题 100】79. 单词搜索

问题背景

给定一个 m × n m \times n m×n 二维字符网格 b o a r d board board 和一个字符串单词 w o r d word word。如果 w o r d word word 存在于网格中,返回 t r u e true true;否则,返回 f a l s e false false
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

数据约束

  • m = b o a r d . l e n g t h m = board.length m=board.length
  • n = b o a r d [ i ] . l e n g t h n = board[i].length n=board[i].length
  • 1 ≤ m , n ≤ 6 1 \le m, n \le 6 1m,n6
  • 1 ≤ w o r d . l e n g t h ≤ 15 1 \le word.length \le 15 1word.length15
  • b o a r d board board w o r d word word 仅由大小写英文字母组成

解题过程

这题放在了回溯这个类别下,其实更适合当作图的深度优先遍历相关问题来解决,可以参考模板题 岛屿数量。
一般做法的框架是定义四个方向,对图中每个节点都尝试搜索它的相邻节点,在不越界且并且和字符串相应位置的字符相同时,进一步递归检查。
有两个可以优化的地方。
一是通过遍历搭配哈希表来统计图中和字符串中每种字符出现的次数,一旦发现字符串中所要求的某种字符出现的次数大于图中对应字符出现的次数,就不可能满足条件,可以避免深搜,直接返回结果。
这样做的时间消耗是 O ( N 2 ) O(N ^ 2) O(N2),要比暴力搜索的指数级好得多。
在此基础上,考虑到返回 f a l s e false false 的情况是某个字符处不匹配造成的,并且遍历字符串的顺序对结果没有影响可以比较字符串中第一个字符和最后一个字符出现的次数,从要求比较高的那一头开始搜索,这样更容易出现不满足条件返回的情形,可以有效减少递归搜索的次数。

具体实现

class Solution {
    private static final int[][] DIRECTIONS = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};

    public boolean exist(char[][] board, String word) {
        // 统计图中每种字符出现的次数
        int[] count = new int[128];
        for(char[] row : board) {
            for(char c : row) {
                count[c]++;
            }
        }
        // 优化一,统计字符串中每种字符要求的次数,如果大于图中出现的相应字符数,直接返回 false
        char[] chW = word.toCharArray();
        int[] wordCount = new int[128];
        for(char c : chW) {
            if(++wordCount[c] > count[c]) {
                return false;
            }
        }
        // 优化二,颠倒单词顺序不影响结果,从出现次数多的那头开始搜索
        if(count[chW[chW.length - 1]] < count[chW[0]]) {
            chW = new StringBuilder(word).reverse().toString().toCharArray();
        }
        // 常规框架,对图中的每个字符位置都尝试进行搜索
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                if(dfs(i, j, 0, board, chW)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int x, int y, int i, char[][] board, char[] word) {
        if(board[x][y] != word[i]) {
            return false;
        }
        // 注意,执行到这里的时候已经判断了图中当前字符和字符串对应位置的字符相同,到达了边界
        if(i == word.length - 1) {
            return true;
        }
        // 修改图,用来标记已访问过
        board[x][y] = 0;
        for(int[] direction : DIRECTIONS) {
            int nextX = x + direction[0];
            int nextY = y + direction[1];
            // 只有在不越界且进一步递归也返回 true 的情况下,当前位置才能返回 true
            if(0 <= nextX && nextX < board.length && 0 <= nextY && nextY < board[0].length && dfs(nextX, nextY, i + 1, board, word)) {
                return true;
            }
        }
        // 恢复现场
        board[x][y] = word[i];
        return false;
    }
}

http://www.kler.cn/a/463409.html

相关文章:

  • Qemu配置QXL显卡支持分辨率
  • Leetcode731. 我的日程安排表 II
  • 安卓11 SysteUI添加按钮以及下拉状态栏的色温调节按钮
  • 38 Opencv HOG特征检测
  • 【前端】Node.js使用教程
  • 算法解析-经典150(双指针、滑动窗口)
  • Amazon Bedrock 实践 - 利用 Llama 3.2 模型分析全球糖尿病趋势
  • uni-app开发-识图小程序-分类识别功能
  • [微服务] - MQ高级
  • 游戏引擎学习第69天
  • doris:基于 Arrow Flight SQL 的高速数据传输链路
  • 小红书ip属地是怎么更新的
  • redis如何实现延时队列
  • 基于单片机的无线智能台灯(论文+源码)
  • STM32单片机芯片与内部54 AT24C02读写 硬件IIC 标准库 HAL库
  • 生态碳汇涡度相关监测与通量数据分析
  • 【Python】selenium结合js模拟鼠标点击、拦截弹窗、鼠标悬停方法汇总(使用 execute_script 执行点击的方法)
  • uniapp——微信小程序,从客户端会话选择文件
  • Linux | 零基础Ubuntu解压RaR等压缩包文件
  • 【MySQL高级】第1-4章
  • Spring Boot教程之四十五:使用 Jackson 在 Spring Boot 的 REST API 实现中使用 JSON
  • 【每日学点鸿蒙知识】弹窗封装成方法、Tab设置默认tabcontent、rawfile文件路径、默认组件宽高、不同状态颜色
  • TypeScript 后端开发中的热重载编译处理
  • Linux(Ubuntu)下ESP-IDF下载与安装完整流程(1)
  • 基于canvas实现的图片加水印功能
  • 单片机从入门到放弃教程001