LeetCode题练习与总结:单词搜索Ⅱ--212

一、题目描述

给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。

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

示例 1:

输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]

示例 2:

输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] 是一个小写英文字母
  • 1 <= words.length <= 3 * 10^4
  • 1 <= words[i].length <= 10
  • words[i] 由小写英文字母组成
  • words 中的所有字符串互不相同

二、解题思路

这个问题可以使用深度优先搜索(DFS)结合前缀树(Trie)来解决。首先,我们构建一个前缀树,将所有的单词插入到前缀树中。然后,我们遍历二维网格的每一个单元格,从每个单元格开始,使用深度优先搜索在网格中寻找匹配前缀树的单词。

以下是具体的步骤:

  1. 构建前缀树:创建一个Trie类,包含插入和查找方法。将所有的单词插入到前缀树中。

  2. 深度优先搜索:创建一个DFS方法,该方法接受当前单元格的位置、前缀树节点、网格、以及已经访问过的单元格集合。在DFS过程中,如果当前单元格的字符不在前缀树中,返回;否则,检查当前节点是否是一个单词的结尾,如果是,则将该单词添加到结果集中,并从前缀树中删除该单词,以避免重复添加。

  3. 遍历网格:遍历网格的每一个单元格,从每个单元格开始进行DFS搜索。

三、具体代码

class Solution {class TrieNode {TrieNode[] children = new TrieNode[26];String word;}class Trie {TrieNode root = new TrieNode();public void insert(String word) {TrieNode node = root;for (char c : word.toCharArray()) {if (node.children[c - 'a'] == null) {node.children[c - 'a'] = new TrieNode();}node = node.children[c - 'a'];}node.word = word;}}public List<String> findWords(char[][] board, String[] words) {Trie trie = new Trie();for (String word : words) {trie.insert(word);}List<String> res = new ArrayList<>();for (int i = 0; i < board.length; i++) {for (int j = 0; j < board[0].length; j++) {dfs(board, i, j, trie.root, res);}}return res;}private void dfs(char[][] board, int i, int j, TrieNode node, List<String> res) {if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] == '#' || node.children[board[i][j] - 'a'] == null) {return;}char c = board[i][j];node = node.children[c - 'a'];if (node.word != null) {res.add(node.word);node.word = null; // 避免重复添加}board[i][j] = '#'; // 标记为已访问dfs(board, i - 1, j, node, res);dfs(board, i + 1, j, node, res);dfs(board, i, j - 1, node, res);dfs(board, i, j + 1, node, res);board[i][j] = c; // 恢复现场}
}

在上述代码中,我们首先构建了一个Trie树,然后通过DFS在网格中搜索匹配的单词。在DFS过程中,我们使用#字符来标记已经访问过的单元格,以避免重复访问。当一个单词被找到时,我们从Trie树中移除该单词,确保不会重复添加到结果集中。最后,我们恢复单元格的原始字符,以便其他搜索路径可以使用该单元格。

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 构建Trie树:

    • 对于每个单词,我们遍历其所有字符并将其插入到Trie树中。假设单词列表words中总共有N个单词,每个单词的平均长度为L,则构建Trie树的时间复杂度为O(N * L)
  • 深度优先搜索(DFS):

    • 对于网格中的每个单元格,我们可能都会执行一次DFS搜索。网格大小为m * n,每个单元格最多会被访问4次(上、下、左、右)。
    • 在最坏的情况下,每次DFS搜索都会遍历整个网格,即每次DFS的时间复杂度为O(m * n)
    • 因此,所有DFS搜索的总时间复杂度为O(m * n * 4 * m * n),即O(4 * m^2 * n^2),简化后为O(m^2 * n^2)

综上所述,总的时间复杂度为构建Trie树的时间复杂度加上DFS的时间复杂度,即O(N * L + m^2 * n^2)

2. 空间复杂度
  • Trie树:

    • Trie树的空间复杂度取决于单词的数量和长度。在最坏的情况下,如果所有单词都是独特的,Trie树将包含所有单词的字符,空间复杂度为O(N * L)
  • DFS搜索:

    • DFS搜索需要递归栈空间,在最坏的情况下,递归深度为网格大小m * n,因此递归栈空间复杂度为O(m * n)
  • 结果列表:

    • 结果列表存储找到的单词,其空间复杂度取决于找到的单词数量,但不会超过单词列表的总数N,因此空间复杂度为O(N)

综上所述,总的空间复杂度为Trie树的空间复杂度加上DFS递归栈的空间复杂度加上结果列表的空间复杂度,即O(N * L + m * n + N)。由于N * L通常是最大的,因此可以简化为O(N * L)

五、总结知识点

  • 数据结构 - Trie树(前缀树)

    • Trie树是一种用于检索字符串数据集中的键的有序树结构,是一种高效的字符串查找数据结构。
    • 每个节点包含一个字符数组children,用于存储子节点,子节点的索引对应字符与’a’的差值。
    • 每个节点可能包含一个字符串word,用于标记该节点是否是某个单词的结束。
  • 递归 - 深度优先搜索(DFS)

    • DFS是一种用于遍历或搜索树或图的算法。
    • 在DFS中,代码通过递归函数dfs来探索所有可能的路径。
    • DFS的递归终止条件包括越界检查、已访问标记检查以及当前字符是否存在于Trie树中的子节点。
  • 字符串处理

    • 字符串转换为字符数组word.toCharArray(),以便遍历字符串中的每个字符。
    • 字符到索引的转换c - 'a',用于访问Trie树中相应的子节点。
  • 数组操作

    • 使用二维字符数组board表示网格。
    • 数组索引访问,例如board[i][j],用于访问网格中的单元格。
  • 集合操作

    • 使用List<String>来存储找到的单词。
    • 使用ArrayList作为具体的列表实现,提供动态数组的功能。
  • 逻辑控制

    • if语句用于条件判断,例如检查边界条件、字符是否匹配Trie树节点。
    • for循环用于遍历网格的每个单元格和单词列表的每个单词。
  • 标记与恢复

    • 使用特殊字符'#'来标记网格中的单元格已被访问,防止在DFS中重复访问。
    • 在完成当前路径的搜索后,恢复单元格的原始字符,以便其他路径可以使用该单元格。
  • 代码优化

    • 在找到单词后,立即将Trie树中对应节点的word字段设置为null,避免在后续搜索中重复添加相同的单词。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/52747.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中智科学技术评价研究中心与中企数研究院实现全面合作

8月29日&#xff0c;中智科学技术评价研究中心与《中国企业报》集团数字化发展研究院在北京顺喜山庄成功举办“数字经济GBC生态系统管理平台”项目实施落地座谈会及研究院高层管理集训班&#xff0c;并签署了项目合作协议。此次合作标志着双方将在“数字中国发展战略”的大背景…

人工智能领域正经历模型规模变革,小型语言模型(SLM)崛起,挑战“规模至上”观念。

在人工智能领域&#xff0c;一场关于模型规模的深刻变革正在悄然发生。长久以来&#xff0c;科技巨头们热衷于庞大语言模型&#xff08;LLM&#xff09;的开发竞赛&#xff0c;但如今&#xff0c;小型语言模型&#xff08;SLM&#xff09;正以其独特的优势逐步崭露头角&#xf…

WordNet介绍——一个英语词汇数据库

传统语义知识库最常见的更新方法是依赖人工手动更新&#xff0c;使用这种更新方法的语义知识库包括最早的 WordNet、FrameNet和 ILD&#xff0c;以及包含丰富内容的 ConceptNet和 DBPedia。此类语义知识库的特点是以单词作为语义知识库的基本构成元素&#xff0c;以及使用预先设…

Linux安装Hadoop(单机版)详细教程

目录 一、JDK安装 1、下载JDK安装包 2、解压下载的JDK安装包 3、移动并重命名JDK包 4、配置Java环境变量 5、验证安装是否成功 二、Hadoop安装 1、下载Hadoop安装包 2、解压Hadoop安装包 3、配置Hadoop环境变量 4、修改配置文件 5、验证Hadoop是否安装成功 三&…

代码随想录——回文子串(Leetcode 647)

题目链接 我的题解&#xff08;双指针&#xff09; 思路&#xff1a; 当然&#xff0c;以下是对您提供的代码的解释&#xff1a; class Solution {public int countSubstrings(String s) {// 初始化回文子字符串的数量int count 0;// 遍历字符串的每个字符&#xff0c;使用…

sicp每日一题[1.38]

Exercise 1.38 In 1737, the Swiss mathematician Leonhard Euler published a memoir D e F r a c t i o n i b u s C o n t i n u i s De\ Fractionibus\ Continuis De Fractionibus Continuis, which included a continued fraction expansion for e − 2 e − 2 e−2, wh…

NCH DrawPad Pro for Mac/Win:强大的图像编辑处理软件

NCH DrawPad Pro for Mac/Win是一款功能全面的图像编辑和设计软件&#xff0c;专为Mac和Windows用户设计。它不仅适用于专业设计师&#xff0c;也深受业余爱好者和创意工作者的喜爱。DrawPad Pro凭借其丰富的绘图工具、强大的编辑功能和便捷的模板库&#xff0c;为用户提供了卓…

Android JNI 设置环境变量

setenv 在 Android JNI 中&#xff0c;setenv 是一个 C 标准库函数&#xff0c;用于设置环境变量。你可以通过 JNI 调用这个函数来设置或修改环境变量&#xff1a; #include <jni.h> #include <cstdlib> // For setenvextern "C" JNIEXPORT void JNICA…

OpenCV杂项图像变换(2)线性混合函数blendLinear()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 执行两个图像的线性混合&#xff1a; dst ( i , j ) weights1 ( i , j ) ∗ src1 ( i , j ) weights2 ( i , j ) ∗ src2 ( i , j ) \texttt{…

策略模式+模版方法模式+简单工厂模式混用优化代码复杂分支问题

说明 这篇博客是在复杂场景使用策略和工厂模式代替分支语句升级版&#xff0c;增加了模版方法模式。将支付类的公共逻辑抽取到模板类中&#xff0c;使整个支付逻辑更加灵活&#xff0c;进一步优化了代码结构&#xff0c;提升了软件的可维护性和可读性。 流程图如下 先看一遍流…

.NET 多版本兼容的精美 WinForm UI控件库

目录 前言 项目介绍 项目使用 项目源码 项目案例 项目组件 项目地址 前言 有粉丝小伙伴在后台留言咨询有没有WinForm 控件库推荐&#xff0c;现在就给安排上。 .NET 平台进行 Windows 应用程序开发的我们来说&#xff0c;找一个既美观又实用的 WinForm UI 控件库至关重…

STM32通过ADM3222完成UART转232通信电平转换

1、简介 单片机默认串口输出电平是UART信号,但是在实际项目中经常需要将其转换成232电平,此时就需要ADM3222芯片来完成电平的转换,下面对使用过程进行总结。 2、硬件电路 从上图中可以看到芯片需要对1、18进行配置才能进行工作,通过查阅手册可知,1引脚需要配置低电平,…

Java算法之循环排序(Cyclic Sort)

简介 循环排序&#xff08;Cyclic Sort&#xff09;是一种最小化移动次数的原地排序算法&#xff0c;它利用了数组元素的初始顺序。如果一个元素不在它最终应该在的位置&#xff0c;循环排序会找到这个元素应该在的位置&#xff0c;并将该位置的元素交换过来&#xff0c;直到这…

【react】常用插件收集

Redux状态管理 - reduxjs/toolkit 、 react-redux react-router-dom: 路由 antd-mobile: 移动端组件库 axios:请求插件 dayjs: 时间处理 classnames: class类名处理 Lodash&#xff1a;遍历数据等 地址→ "dependencies": {"reduxjs/toolkit": &quo…

【Ubuntu】Ubuntu 24 配置镜像源

【Ubuntu】Ubuntu 24 配置镜像源 零、起因 最近在虚拟机中安装了个ubuntu-24.04-desktop-amd64&#xff0c;默认是国外的软件源&#xff0c;很慢&#xff0c;故替换到国内。 壹、替换 源地址&#xff08;阿里源&#xff09; https://developer.aliyun.com/mirror/ubuntu …

云计算实训37——Dockerfile的应用+私有仓库的创建与管理

一、文件联合系统 文件联合系统&#xff08;Overlay Filesystem&#xff09;&#xff1a; 是一种允许多个文件系统层叠在一起的文件系统。它通常用于将只读文件系统&#xff08;如基础镜像&#xff09;与可写文件系统&#xff08;如用户的修改&#xff09;结合&#xff0c;形…

【3D目标检测】MMdetection3d——nuScenes数据集训练BEVFusion

引言 MMdetection3d&#xff1a;【3D目标检测】环境搭建&#xff08;OpenPCDet、MMdetection3d&#xff09; MMdetection3d源码地址&#xff1a;https://github.com/open-mmlab/mmdetection3d/tree/main?tabreadme-ov-file IS-Fusion源码地址&#xff1a;https://github.co…

dbc转换成excel

‌要将DBC文件转换为Excel格式&#xff0c;可以使用Canoe软件进行导出。‌ 使用Canoe软件将DBC文件导出为Excel格式的具体步骤如下&#xff1a; 打开Canoe软件&#xff0c;并在项目工程中加载或创建一个DBC文件。在主菜单中选择“文件”>“导出”>“数据库”选项。在打…

【Scala】Windows下安装Scala(全面)

目录 1.下载 2.安装 3.配置环境变量 1.新增系统环境变量 2.环境变量Path 4.验证 1.下载 官网下载地址&#xff1a;https://downloads.lightbend.com/scala/2.11.12/scala-2.11.12.msi 2.安装 双击下载的.msi文件&#xff1a; 勾选"I accept the terms in the Li…

前后端交互的路径怎么来的?后端解决cors问题的一种方法

背景&#xff1a;后端使用node.js搭建&#xff0c;用的是express 前端请求的路径baseURL怎么来的 &#xff1f; 前后端都在同一台电脑上运行&#xff0c;后端的域名就是localhost&#xff0c;如果使用的是http协议&#xff0c;后端监听的端口号为3000&#xff0c;那么前端请求…