单词接龙[中等]

一、题目

字典wordList中从单词beginWordendWord的 转换序列 是一个按下述规格形成的序列beginWord -> s1 -> s2 -> ... -> sk
1、每一对相邻的单词只差一个字母。
2、对于1 <= i <= k时,每个si都在wordList中。注意,beginWord不需要在wordList中。
3、sk == endWord

给你两个单词beginWordendWord和一个字典wordList,返回从beginWordendWord的最短转换序列中的单词数目 。如果不存在这样的转换序列,返回0

示例 1:
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:5
解释:一个最短转换序列是hit->hot-> dot -> dog -> cog, 返回它的长度5

示例 2:
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:0
解释:endWord cog不在字典中,所以无法进行转换。

1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWordendWordwordList[i]由小写英文字母组成
beginWord != endWord
wordList中的所有字符串 互不相同

二、代码

【1】广度优先搜索 + 优化建图: 本题要求的是最短转换序列的长度,看到最短首先想到的就是广度优先搜索。想到广度优先搜索自然而然的就能想到图,但是本题并没有直截了当的给出图的模型,因此我们需要把它抽象成图的模型。我们可以把每个单词都抽象为一个点,如果两个单词可以只改变一个字母进行转换,那么说明他们之间有一条双向边。因此我们只需要把满足转换条件的点相连,就形成了一张图。

基于该图,我们以beginWord为图的起点,以endWord为终点进行广度优先搜索,寻找beginWordendWord的最短路径。

基于上面的思路我们考虑如何编程实现。首先为了方便表示,我们先给每一个单词标号,即给每个单词分配一个id。创建一个由单词wordid对应的映射wordId,并将beginWordwordList中所有的单词都加入这个映射中。之后我们检查endWord是否在该映射内,若不存在,则输入无解。我们可以使用哈希表实现上面的映射关系。

然后我们需要建图,依据朴素的思路,我们可以枚举每一对单词的组合,判断它们是否恰好相差一个字符,以判断这两个单词对应的节点是否能够相连。但是这样效率太低,我们可以优化建图。具体地,我们可以创建虚拟节点。对于单词hit,我们创建三个虚拟节点*ith*thi*,并让hit向这三个虚拟节点分别连一条边即可。如果一个单词能够转化为hit,那么该单词必然会连接到这三个虚拟节点之一。对于每一个单词,我们枚举它连接到的虚拟节点,把该单词对应的id与这些虚拟节点对应的id相连即可。

最后我们将起点加入队列开始广度优先搜索,当搜索到终点时,我们就找到了最短路径的长度。注意因为添加了虚拟节点,所以我们得到的距离为实际最短路径长度的两倍。同时我们并未计算起点对答案的贡献,所以我们应当返回距离的一半再加一的结果。

class Solution {Map<String, Integer> wordId = new HashMap<String, Integer>();List<List<Integer>> edge = new ArrayList<List<Integer>>();int nodeNum = 0;public int ladderLength(String beginWord, String endWord, List<String> wordList) {for (String word : wordList) {addEdge(word);}addEdge(beginWord);if (!wordId.containsKey(endWord)) {return 0;}int[] dis = new int[nodeNum];Arrays.fill(dis, Integer.MAX_VALUE);int beginId = wordId.get(beginWord), endId = wordId.get(endWord);dis[beginId] = 0;Queue<Integer> que = new LinkedList<Integer>();que.offer(beginId);while (!que.isEmpty()) {int x = que.poll();if (x == endId) {return dis[endId] / 2 + 1;}for (int it : edge.get(x)) {if (dis[it] == Integer.MAX_VALUE) {dis[it] = dis[x] + 1;que.offer(it);}}}return 0;}public void addEdge(String word) {addWord(word);int id1 = wordId.get(word);char[] array = word.toCharArray();int length = array.length;for (int i = 0; i < length; ++i) {char tmp = array[i];array[i] = '*';String newWord = new String(array);addWord(newWord);int id2 = wordId.get(newWord);edge.get(id1).add(id2);edge.get(id2).add(id1);array[i] = tmp;}}public void addWord(String word) {if (!wordId.containsKey(word)) {wordId.put(word, nodeNum++);edge.add(new ArrayList<Integer>());}}
}

时间复杂度: O(N×C^2)。其中NwordList的长度,C为列表中单词的长度。
1、建图过程中,对于每一个单词,我们需要枚举它连接到的所有虚拟节点,时间复杂度为O(C),将这些单词加入到哈希表中,时间复杂度为O(N×C),因此总时间复杂度为O(N×C)
2、广度优先搜索的时间复杂度最坏情况下是O(N×C)。每一个单词需要拓展出O(C)个虚拟节点,因此节点数O(N×C)
空间复杂度: O(N×C^2)。其中NwordList的长度,C为列表中单词的长度。哈希表中包含O(N×C)个节点,每个节点占用空间O(C),因此总的空间复杂度为O(N×C^2)

双向广度优先搜索: 根据给定字典构造的图可能会很大,而广度优先搜索的搜索空间大小依赖于每层节点的分支数量。假如每个节点的分支数量相同,搜索空间会随着层数的增长指数级的增加。考虑一个简单的二叉树,每一层都是满二叉树的扩展,节点的数量会以2为底数呈指数增长。如果使用两个同时进行的广搜可以有效地减少搜索空间。一边从beginWord开始,另一边从endWord开始。我们每次从两边各扩展一层节点,当发现某一时刻两边都访问过同一顶点时就停止搜索。这就是双向广度优先搜索,它可以可观地减少搜索空间大小,从而提高代码运行效率。

class Solution {Map<String, Integer> wordId = new HashMap<String, Integer>();List<List<Integer>> edge = new ArrayList<List<Integer>>();int nodeNum = 0;public int ladderLength(String beginWord, String endWord, List<String> wordList) {for (String word : wordList) {addEdge(word);}addEdge(beginWord);if (!wordId.containsKey(endWord)) {return 0;}int[] disBegin = new int[nodeNum];Arrays.fill(disBegin, Integer.MAX_VALUE);int beginId = wordId.get(beginWord);disBegin[beginId] = 0;Queue<Integer> queBegin = new LinkedList<Integer>();queBegin.offer(beginId);int[] disEnd = new int[nodeNum];Arrays.fill(disEnd, Integer.MAX_VALUE);int endId = wordId.get(endWord);disEnd[endId] = 0;Queue<Integer> queEnd = new LinkedList<Integer>();queEnd.offer(endId);while (!queBegin.isEmpty() && !queEnd.isEmpty()) {int queBeginSize = queBegin.size();for (int i = 0; i < queBeginSize; ++i) {int nodeBegin = queBegin.poll();if (disEnd[nodeBegin] != Integer.MAX_VALUE) {return (disBegin[nodeBegin] + disEnd[nodeBegin]) / 2 + 1;}for (int it : edge.get(nodeBegin)) {if (disBegin[it] == Integer.MAX_VALUE) {disBegin[it] = disBegin[nodeBegin] + 1;queBegin.offer(it);}}}int queEndSize = queEnd.size();for (int i = 0; i < queEndSize; ++i) {int nodeEnd = queEnd.poll();if (disBegin[nodeEnd] != Integer.MAX_VALUE) {return (disBegin[nodeEnd] + disEnd[nodeEnd]) / 2 + 1;}for (int it : edge.get(nodeEnd)) {if (disEnd[it] == Integer.MAX_VALUE) {disEnd[it] = disEnd[nodeEnd] + 1;queEnd.offer(it);}}}}return 0;}public void addEdge(String word) {addWord(word);int id1 = wordId.get(word);char[] array = word.toCharArray();int length = array.length;for (int i = 0; i < length; ++i) {char tmp = array[i];array[i] = '*';String newWord = new String(array);addWord(newWord);int id2 = wordId.get(newWord);edge.get(id1).add(id2);edge.get(id2).add(id1);array[i] = tmp;}}public void addWord(String word) {if (!wordId.containsKey(word)) {wordId.put(word, nodeNum++);edge.add(new ArrayList<Integer>());}}
}

时间复杂度: O(N×C^2)。其中NwordList的长度,C为列表中单词的长度。
1、建图过程中,对于每一个单词,我们需要枚举它连接到的所有虚拟节点,时间复杂度为O(C),将这些单词加入到哈希表中,时间复杂度为O(N×C),因此总时间复杂度为O(N×C)
2、双向广度优先搜索的时间复杂度最坏情况下是O(N×C)。每一个单词需要拓展出O(C)个虚拟节点,因此节点数O(N×C)

空间复杂度: O(N×C^2)。其中NwordList的长度,C为列表中单词的长度。哈希表中包含O(N×C)个节点,每个节点占用空间O(C),因此总的空间复杂度为O(N×C^2)

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

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

相关文章

Midjourney V6版本的5大新特性,掌握了,想法和实现信手拈来

Midjourney v6已推出&#xff1a;更简单的提示、增强的文本集成和更高水平的照片真实感&#xff01;以下是每个创意人员都需要了解的 5 个重要见解。 一、产品文字整合 使用简单风格提示向您的产品添加文本提示&#xff1a;带有文字“SALMA”的白色健身瓶 Midjourney v5.2&am…

Git安装和使用教程,并以gitee为例实现远程连接远程仓库

文章目录 1、Git简介及安装2、使用方法2.1、Git的启动与配置2.2、基本操作2.2.1、搭建自己的workspace2.2.2、git add2.2.3、git commit2.2.4、忽略某些文件不予提交2.2.5、以gitee为例实现git连接gitee远程仓库来托管代码 1、Git简介及安装 版本控制&#xff08;Revision cont…

只用10分钟,ChatGPT就帮我写了一篇2000字文章

有了ChatGPT之后&#xff0c;于我来说&#xff0c;有两个十分明显的变化&#xff1a; 1. 人变的更懒 因为生活、工作中遇到大大小小的事情&#xff0c;都可以直接找ChatGPT来寻求答案。 2. 工作产出量更大 之前花一天&#xff0c;甚至更久才能写一篇原创内容&#xff0c;现…

qt简单连接摄像头

要使用摄像头&#xff0c;就需要链接多媒体模块以及多媒体工具模块 需要在.pro文件中添加QT multimedia multimediawidgets 是用的库文件 QCamera 类用于打开系统的摄像头设备&#xff0c; QCameraViewfinder 用于显示捕获的视频&#xff0c; QCameraImageCapt…

Java并发工具类---ForkJoin、countDownlatch、CyclicBarrier、Semaphore

一、Fork Join fork join是JDK7引入的一种并发框架&#xff0c;采用分而治之的思想来处理并发任务 ForkJoin框架底层实现了工作窃取&#xff0c;当一个线程完成任务处于空闲状态时&#xff0c;会窃取其他工作线程的任务来做&#xff0c;这样可以充分利用线程来进行并行计算&a…

2023.11.24 信息学日志

2023.11.24 信息学日志 1. CF1700D River Locks题目描述题目概况思路点拨 1. CF1700D River Locks 题目描述 https://www.luogu.com.cn/problem/CF1700D 题目概况 来源&#xff1a;Codeforces 洛谷难度&#xff1a; 绿题 \color{green}绿题 绿题 CF难度&#xff1a; 1900…

系列十四、SpringBoot + JVM参数配置实战调优

一、SpringBoot JVM参数配置实战调优 1.1、概述 前面的系列文章大篇幅的讲述了JVM的内存结构以及各种参数&#xff0c;今天就使用SpringBoot项目实战演示一下&#xff0c;如何进行JVM参数调优&#xff0c;如果没有阅读过前面系列文章的朋友&#xff0c;建议先阅读后再看本篇文…

Java整合APNS推送消息-IOS-APP(基于.p12推送证书)

推送整体流程 1.在开发者中心申请对应的证书&#xff08;我用的是.p12文件&#xff09; 2.苹果手机用户注册到APNS&#xff0c;APNS将注册的token返回给APP&#xff08;服务端接收使用&#xff09;。 3.后台服务连接APNS&#xff0c;获取连接对象 4.后台服务构建消息载体 5.后台…

C++面向对象(OOP)编程-位运算详解

本文主要介绍原码、位运算的种类&#xff0c;以及常用的位运算的使用场景。 目录 1 原码、反码、补码 2 有符号和无符号数 3 位运算 4 位运算符使用规则 4.1 逻辑移位和算术移位 4.1.1 逻辑左移和算法左移 4.1.2 逻辑右移和算术右移 4.1.3 总结 4.2 位运算的应用场景 …

Searching for MobileNetV3(2019)

文章目录 Abstract主要内容实验结果 IntroductionRelated WorkEfficient Mobile Building BlocksNetwork SearchPlatform-Aware NAS for Block-wise SearchNetAdapt for Layer-wise Search Network ImprovementsRedesigning Expensive LayersNonlinearitiesLarge squeeze-and-e…

JAVA设计模式(三)-原型

JAVA设计模式(三)-原型 本篇文章主要讲下java 创建型设计模式中的原型模式. 何谓原型模式: 简单来说就是 将一个对象作为原型&#xff0c;通过对其进行复制而克隆出多个和原型类似的新实例。 使用原型模式,就可以简化实例化的过程, 不必依赖于构造函数或者new关键字. 由于j…

蓝桥杯-每日刷题-027

出租汽车计费器 一、题目要求 题目描述 有一个城市出租汽车的计费规则是3公里内&#xff08;含3公里&#xff09;基本费6元&#xff0c;超过3公里&#xff0c;每一公里1.4元。 现在对于输入具体的公里数x&#xff08;0<x<1000&#xff09;&#xff0c;编程计算x公里所需…

PHP-Xlswriter高性能导出Excel

使用背景 使用传统的PHPExcel导出效率太慢&#xff0c;并且资源占用高&#xff0c;数据量大的情况&#xff0c;会导致服务占用大量的资源&#xff0c;从而导致生产意味&#xff0c;再三思索后&#xff0c;决定使用其他高效率的导出方式 PHP-Xlswriter PHPExcel 因为内存消耗过…

信号与线性系统翻转课堂笔记9——傅里叶变换

信号与线性系统翻转课堂笔记9——傅里叶变换 The Flipped Classroom9 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&#xff09;…

MyBatis中延迟加载,全局和局部的开启使用与关闭

文章目录 MyBatis中延迟加载&#xff0c;全局和局部的开启使用与关闭1、问题提出2、延迟加载和立即加载延迟加载立即加载 3、三种对应的表关系中的加载4、打开全局延迟加载&#xff08;实现一对一的延迟加载&#xff09;5、实现一对多的延迟加载&#xff08;将上面设置的全局延…

零成本搭建一款博客网站(基于Vercel+Hexo完美实现)【保姆级教程】

文章目录 &#x1f438;基于VercelHexo零成本搭建博客网站&#x1f43b;实现思路 &#x1f42e;Hexo的配置与安装&#x1f412;Hexo的美化与使用&#x1f42b;Github的推送与部署&#x1f43c;Vercel部署与网站上线&#x1f41b;总结 &#x1f438;基于VercelHexo零成本搭建博客…

【数据结构】递归与分治

一.递归 1.递归的概念&#xff1a; 子程序&#xff08;或函数&#xff09;. 接调用自己或通过一系列调用语句间接调用自己&#xff0c;成为递归。 递归是一种描述问题和解决问题的基本方法。 重复地把问题转化为与原问题相似的新问题&#xff0c;直到问题解决为止。 2.递归…

[EFI]联想Thinkpad X1 (2020)电脑 Hackintosh 黑苹果efi引导文件

硬件型号驱动情况主板 Thinkpad X1 Carbon 处理器Intel Core i5-10210U (formerly Comet Lake)已驱动内存8GB DDR3 (or something like that)已驱动硬盘WDC PC SN730 SDBQNTY-256G-1001已驱动显卡Intel UHD 620Nvidia GeForce MX250(屏蔽)无法驱动声卡Realtek ALC285已驱动网卡…

面向LLM的App架构——技术维度

这是两篇面向LLM的大前端架构的第二篇&#xff0c;主要写我对LLM辅助开发能力的认知以及由此推演出的适合LLM辅助开发的技术架构。 LLM之于代码 商业代码对质量的要求其实对LLM是有点高的。主要是输入准确度、输出准确度&#xff08;这个是绝大部分人质疑的点&#xff09;、知…

ElasticSearch学习篇9_文本相似度计算方法现状以及基于改进的 Jaccard 算法代码实现

背景 XOP亿级别题库的试题召回以及搜题的举一反三业务场景都涉及使用文本相似搜索技术&#xff0c;学习此方面技术以便更好的服务于业务场景。 目前基于集合的Jaccard算法以及基于编辑距离的Levenshtein在计算文本相似度场景中有着各自的特点&#xff0c;为了优化具体的计算时…