【图论 回溯 广度优先搜索】126. 单词接龙 II

本文涉及知识点

图论 回溯 深度优先搜索 广度优先搜索
图论知识汇总

LeetCode 126. 单词接龙 II

按字典 wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> … -> sk 这样的单词序列,并满足:
每对相邻的单词之间仅有单个字母不同。
转换过程中的每个单词 si(1 <= i <= k)必须是字典 wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
sk == endWord
给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord, s1, s2, …, sk] 的形式返回。

示例 1:
输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:[[“hit”,“hot”,“dot”,“dog”,“cog”],[“hit”,“hot”,“lot”,“log”,“cog”]]
解释:存在 2 种最短的转换序列:
“hit” -> “hot” -> “dot” -> “dog” -> “cog”
“hit” -> “hot” -> “lot” -> “log” -> “cog”
示例 2:

输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出:[]
解释:endWord “cog” 不在字典 wordList 中,所以不存在符合要求的转换序列。

提示:

1 <= beginWord.length <= 5
endWord.length == beginWord.length
1 <= wordList.length <= 500
wordList[i].length == beginWord.length
beginWord、endWord 和 wordList[i] 由小写英文字母组成
beginWord != endWord
wordList 中的所有单词 互不相同

图论

beginWord和wordList对应一个节点,注意beginWord如果和wordList[i]相同,则对应节点也相同。
用哈希映射给单词编号,用字典树也可以。
vDis[i]记录节点i到beginWord的最短路径。
vPre[i]记录i到beginWord的最短路径的倒数第二个节点,如果有多条路径,记录所有路径的倒数第二个节点。
n = wordList.length m= beginWord.length ∑ \sum = 26 26个小写字母
时间复杂度:以下三步之和:
一,建立临接表。O(nnm) ≈ \approx 106
二,广度优先,等于边数,边数最多n × \times ×n 。故时间复杂度O(nn), ≈ \approx 106
三,回溯。计算复杂。怀疑是 ∑ 4 \sum^4 4,即每个节点和endWord相同字符+1,其实不是。如:“hit”,“hot”,“dot”,“dog”,“cog”
hit有三个字符和cog不同,hot dot 有两个字符和cog不同,dog有一个字符和cog不同。

代码

核心代码

class CStrToIndex
{
public:CStrToIndex(const vector<string>& wordList) {for (const auto& str : wordList){Add(str);}}void Add(const string& str){if (m_mIndexs.count(str)) { return; }m_mIndexs[str] = m_strs.size();m_strs.push_back(str);}vector<string> m_strs;int GetIndex(const string& str){if (m_mIndexs.count(str)) { return m_mIndexs[str]; }return -1;}
protected:unordered_map<string, int> m_mIndexs;
};
class Solution {
public:vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {CStrToIndex inx(wordList);inx.Add(beginWord);m_c = inx.m_strs.size();vector<vector<int>> vNeiBo(m_c);for (int i = 0; i < m_c; i++) {for (int j = i + 1; j < m_c; j++) {int iNotSame = 0;for (int k = 0; k < inx.m_strs[i].length(); k++) {iNotSame += (inx.m_strs[i][k] != inx.m_strs[j][k]);}if (1 == iNotSame) {vNeiBo[i].emplace_back(j);vNeiBo[j].emplace_back(i);}}}m_iBegin = inx.GetIndex(beginWord);		m_iEnd = inx.GetIndex(endWord);if (-1 == m_iEnd) { return {}; };queue<int> que;vector<int> dis(m_c,m_c);vector<vector<int>> vPre(m_c);auto Add = [&](int cur, int next) {const int iNew = dis[cur] + 1;if (iNew > dis[next]) { return; }			if (iNew < dis[next]) {vPre[next].clear();dis[next] = iNew;que.emplace(next);}vPre[next].emplace_back(cur);};dis[m_iBegin] = 0;que.emplace(m_iBegin);while (que.size()) {auto cur = que.front();que.pop();for (const auto& next : vNeiBo[cur]) {Add(cur, next);}}BackTrack(m_iEnd, inx, vPre);if (dis[m_iEnd] >= m_c) { return {}; }return m_vRet;}void BackTrack(int cur, CStrToIndex& inx, const vector<vector<int>>& vPre){if (m_iBegin == cur) {m_vCur.emplace_back(cur);m_vRet.emplace_back();for (auto it = m_vCur.rbegin(); it != m_vCur.rend(); ++it) {m_vRet.back().emplace_back(inx.m_strs[*it]);}m_vCur.pop_back();}m_vCur.emplace_back(cur);for (const auto& pre : vPre[cur]){BackTrack(pre,inx, vPre);}m_vCur.pop_back();}vector<vector<string>> m_vRet;vector<int> m_vCur;int m_c, m_iBegin,m_iEnd;
};

测试用例

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){assert(v1[i] == v2[i]);}
}template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}int main()
{string beginWord, endWord;vector<string> wordList;{Solution slu;beginWord = "red", endWord = "tax", wordList = { "ted","tex","red","tax","tad","den","rex","pee" };auto res = slu.findLadders(beginWord, endWord, wordList);Assert({ {"red","ted","tex","tax"},{"red","rex","tex","tax"},{"red","ted","tad","tax"} }, res);}{Solution slu;beginWord = "hit", endWord = "cog", wordList = { "hot","dot","dog","lot","log" };auto res = slu.findLadders(beginWord, endWord, wordList);Assert({  }, res);}{Solution slu;beginWord = "hit", endWord = "cog", wordList = { "hot","dot","dog","lot","log","cog" };auto res = slu.findLadders(beginWord, endWord, wordList);Assert({ {"hit","hot","dot","dog","cog"},{"hit","hot","lot","log","cog"} }, res);}{Solution slu;beginWord = "a", endWord = "c", wordList = { "a","b","c" };auto res = slu.findLadders(beginWord, endWord, wordList);Assert({ {"a","c"} }, res);}}

2023年4月版

class Solution {
public:vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {if (wordList.end() == std::find(wordList.begin(), wordList.end(), beginWord)){wordList.emplace_back(beginWord);}for (const auto& word : wordList){AddNeib(word);}vector<vector<std::string>> vRet;std::queue<int> preQue;m_vDis.resize(m_vNeib.size());const int iBeginIndex = m_mMaskIndex[StrToMask(beginWord)];m_vDis[iBeginIndex] = 1;preQue.emplace(iBeginIndex);const long long llMask = StrToMask(endWord);if (0 == m_mMaskIndex.count(llMask)){return vRet;}const int iEndIndex = m_mMaskIndex[llMask];for (int i = 1; preQue.size(); i++){std::queue<int> curQue;while (preQue.size()){const auto curIndex = preQue.front();preQue.pop();if (curIndex == iEndIndex){vector<string> strs((i+1)/2);dfs(vRet, strs, iEndIndex, i);return vRet;}for (const auto & next : m_vNeib[curIndex]){if (m_vDis[next]){continue;}m_vDis[next] = i + 1;curQue.emplace(next);}}preQue.swap(curQue);}return vRet;}void dfs(std::vector<std::vector<string>>& vRet, std::vector<string>& strs, int iCurNode, int iCurLeve){if (iCurLeve & 1){strs[(iCurLeve - 1) / 2] = m_vStrs[iCurNode];if (1 == iCurLeve){vRet.emplace_back(strs);return;}}for (const auto& next : m_vNeib[iCurNode]){if (1 + m_vDis[next] != iCurLeve){continue;}dfs(vRet, strs, next, iCurLeve - 1);}}long long StrToMask(const string& s){long long llRet = 0;for (const auto& ch : s){llRet = llRet * m_iUnit + ch - 'a' + 1;}return llRet;}string MaskToStr(long long llMask){vector<char> chas;while (llMask){chas.emplace_back(llMask%m_iUnit - 1 + 'a');llMask /= m_iUnit;}std::reverse(chas.begin(), chas.end());chas.emplace_back(0);return std::string(chas.begin(), chas.end());}int AddWord(const string& s){return AddWord(StrToMask(s));}int AddWord(long long llMask){if (m_mMaskIndex.count(llMask)){return m_mMaskIndex[llMask];}m_vNeib.emplace_back();m_vStrs.emplace_back(MaskToStr(llMask));return m_mMaskIndex[llMask] = m_vNeib.size()-1;}void AddNeib(const string& s){		const long long llMask = StrToMask(s);int index = AddWord(llMask);long long llMul = 1;for (int i = 0; i < s.length(); i++){const char& ch = s[s.length() - 1 - i];auto tmp = llMask - llMul*(ch - 'a' + 1);int index2 = AddWord(tmp);m_vNeib[index].emplace_back(index2);m_vNeib[index2].emplace_back(index);llMul *= m_iUnit;}}std::unordered_map<long long, int> m_mMaskIndex;std::vector<vector<int>> m_vNeib;std::vector<std::string> m_vStrs;vector<int> m_vDis;const int m_iUnit = 27;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
《喜缺全书算法册》以原理、正确性证明、总结为主。
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

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

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

相关文章

新能源行业网间数据交换,更好用更专业的工具是什么?

新能源行业涵盖了多个方面&#xff0c;包括但不限于新能源汽车、可再生能源技术等。新能源行业发展具有重要的意义&#xff0c;新能源企业的研发数据极其重要&#xff0c;为了保障网络安全和数据安全&#xff0c;许多新能源企业采用逻辑隔离的方式进行网络隔离&#xff0c;此时…

【网络基础】网络层 之 IP协议与分片、网段划分、IP地址分类、子网掩码与路由

文章目录 网络层1. IP协议段格式1.1 分片1.2 *为什么存在分片 / 分片是什么 ?*1.3 *如何理解 / 实现 分片与组装*1.4 深入具体&#xff1a;分片 和 组装 的过程1.5 为什么不推荐 分片 2. 网段划分2.1 举例&#xff1a;国际间通信 && 国家内通信2.2 理解网段划分 3. IP…

[Kubernetes] Istio on Kubernetes 实践

文章目录 1.Kubernetes 创建2.Istio 部署2.1 下载 Istio2.2 安装 Istio 3.Istio on Kubernetes 实践3.1 部署 Bookinfo 示例应用3.2 确定入站 IP 和端口 1.Kubernetes 创建 主机名内部ip外部ipmaster192.168.66.2139.198.36.40node1192.168.66.3139.198.1.192node2192.168.66.…

传输层的端到端的流量控制和网络层的流量控制,有什么区别,他们互补在什么地方

传输层的端到端流量控制和网络层的流量控制在以下几个方面有所区别&#xff0c;并在一定程度上相互补充&#xff1a; 控制对象&#xff1a; 传输层的端到端流量控制主要关注单个会话或连接的流量。它在发送端和接收端之间进行&#xff0c;确保发送方不会发送过多的数据&#xf…

Ps 滤镜:粉笔和炭笔

Ps菜单&#xff1a;滤镜/滤镜库/素描/粉笔和炭笔 Filter Gallery/Sketch/Chalk & Charcoal 粉笔和炭笔 Chalk & Charcoal滤镜可以模拟传统的粉笔和炭笔画风格&#xff0c;通过特定的纹理和线条重绘图像的高光、中间色调和阴影区域。此滤镜非常适合于为数字图像添加手绘…

璩静是为了薅百度羊毛

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 百度副总裁璩静离职了&#xff0c;网传她的年薪是1500万&#xff0c;而璩静在4月24日注册了一个文化传媒公司&#xff0c;大家都认为璩静是在为离职做准备。但松松我认为不是。 我认为&#xff1a;璩静成立新公司是…

[力扣题解]53. 最大子数组和

题目&#xff1a;53. 最大子数组和 思路 贪心法&#xff1b; 从头到尾遍历&#xff0c;对遇见的元素求和&#xff0c;如果和<0&#xff0c;就重新开始&#xff0c;因为前一段是负数&#xff0c;对我们希望的最大的和是没有帮助的&#xff1b; 代码 // 贪心法 // 和<0…

组织机构树形列表实现

源码地址&#xff1a;https://www.lanzouw.com/itjDc1ydraof 本来上传了源码&#xff0c;但是发现只能VIP才能下载&#xff0c;所以重新上传到蓝奏云上了&#xff0c;链接如下&#xff1a; 先看下效果图&#xff1a; 可以自己写HTML来自定义每一项的内容显示&#xff0c;包括…

物联网到底物联了个啥?——青创智通

工业物联网解决方案-工业IOT-青创智通 物联网&#xff0c;这个听起来似乎颇具科技感和未来感的词汇&#xff0c;其实早已悄然渗透到我们生活的方方面面。从智能家居到智慧城市&#xff0c;从工业自动化到医疗健康&#xff0c;物联网技术正在以其独特的魅力改变着我们的生活方式…

2024.04.30校招 实习 内推 面经

绿*泡*泡VX&#xff1a; neituijunsir 交流*裙 &#xff0c;内推/实习/校招汇总表格 1、校招&实习 | 零跑汽车校园招聘 - 三电研发专场&#xff08;内推&#xff09; 校招&实习 | 零跑汽车校园招聘 - 三电研发专场&#xff08;内推&#xff09; 2、校招丨海尔智家2…

【ruoyi】docker部署 captchaImage接口 FontConfiguration空指针异常

后台服务报错captchaImage接口空指针异常&#xff0c;无法启动项目&#xff1a; [http-nio-4431-exec-27] ERROR c.r.f.w.e.GlobalExceptionHandler - [handleRuntimeException,93] - 请求地址/captchaImage,发生未知异常.java.lang.NullPointerException: nullat sun.awt.Font…

鸿蒙开发-ArkTS语言-XML

鸿蒙开发-UI-web 鸿蒙开发-UI-web-页面 鸿蒙开发-ArkTS语言-基础类库 鸿蒙开发-ArkTS语言-并发 鸿蒙开发-ArkTS语言-并发-案例 鸿蒙开发-ArkTS语言-容器 鸿蒙开发-ArkTS语言-非线性容器 文章目录 前言 一、XML概述 二、XML生成 三、XML解析 1.解析XML标签和标签值 2.解析XML属性…

conan2 基础入门(05)-(静态库动态库)(DebugRelease)

conan2 基础入门(05)-(静态库&动态库)(Debug&Release) 文章目录 conan2 基础入门(05)-(静态库&动态库)(Debug&Release)⭐准备预备文件和Code ⭐静态库&动态库静态库动态库 ⭐Debug&ReleaseReleaseDebug END视频教学settings.yml ⭐准备 本文均在windo…

let和const命令

2.1.1 基本用法 ES6新增了let命令&#xff0c;用于生命变量。其用法类似于var&#xff0c;但是所声明的变量只在let命令所在的代码块内有效。 {let a 10&#xff1b;var b 1&#xff1b; }a//ReferenceError&#xff1a;a is not defined b//1上面的代码在代码块中分别用le…

iOS LQG开发框架(持续更新)

基本规则 开发便利性为前提&#xff0c;妥协性能可维护性为前提可读性MVC各部分职责一定要清晰&#xff0c;controll类里面功能尽量抽离成helper&#xff0c;功能一定要清晰&#xff0c;这个非常重要&#xff0c;对代码可读性提升非常高方法内部尽量使用局部变量&#xff0c;最…

【Vue管理后台】用户登录强制修改密码

【Ruoyi管理后台】用户登录强制修改密码 这篇文章已经实现了这个需求&#xff0c;因为个人业务中强制改密页面是进入系统且浮动于其他页面上方&#xff0c;进入强制改密页面的同时其他页面会向后台请求&#xff0c;如果清空token后会造成其他的请求失败。本文整体步骤与参考博文…

经验报告 - SCADE Suite 6 KCG 经历 DO-178B 认证过程中进行的活动

详细内容可参考 ICFP’09 《Experience Report: Using Objective Caml to Develop Safety-Critical Embedded Tools in a Certification Framework》。 数十年前&#xff0c;民用航空电子机构就定义了机载嵌入式代码的认证要求。DO-178B 标准&#xff08;RTCA/DO-178B&#xff…

5.神经网络-激活函数

目录 1. 激活函数不是阶跃函数 1.1 激活函数和阶跃函数都是非线性函数 1.2 激活函数不是阶跃函数 2. sigmoid 函数 2.1 sigmoid 函数表达式 2.2 sigmoid 函数 Python 实现 2.4 sigmoid 函数图 3. ReLU 函数 3.1 ReLU 函数表达式 3.2 ReLU 函数 Python 实现 3.4 ReLU…

线性集合:ArrayList,LinkedList,Vector/Stack

共同点&#xff1a;都是线性集合 ArrayList ArrayList 底层是基于数组实现的&#xff0c;并且实现了动态扩容&#xff08;当需要添加新元素时&#xff0c;如果 elementData 数组已满&#xff0c;则会自动扩容&#xff0c;新的容量将是原来的 1.5 倍&#xff09;&#xff0c;来…

Rust使用HashSet对Vec类型的元素进行去重

在Rust语言中&#xff0c;对Vec类型的元素进行去重&#xff0c;一种常见的方法是使用一个HashSet来帮助我们快速检查元素是否已经存在。以下是使用HashSet对Vec进行去重的示例代码&#xff1a; use std::collections::HashSet;fn main() {let vec_numbers vec![1, 2, 2, 3, 4…