【图论 回溯 广度优先搜索】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.hqwc.cn/news/691154.html

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

相关文章

【简单介绍下Sass】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

C++(week3):C语言文件操作

文章目录 (十二) 文件1.流(1)流模型(2)程序员视角的文件(3)缓冲区类型(4)标准流(5)二进制文件 与 文本文件(6)文件流的接口(API) 2.打开/关闭文件(1)fopen(2)fclose(3)示例代码 3.读/写文件(1)fgetc / fputc&#xff1a;一个字符一个字符地读写(2)fgets / fputs&#xff1a;一行…

Java 【数据结构】 哈希(Hash超详解)HashSetHashMap【神装】

登神长阶 第十神装 HashSet 第十一神装 HashMap 目录 &#x1f454;一.哈希 &#x1f9e5;1.概念 &#x1fa73;2.Object类的hashCode()方法: &#x1f45a;3.String类的哈希码: &#x1f460;4.注意事项: &#x1f3b7;二.哈希桶 &#x1fa97;1.哈希桶原理 &#x…

llm.c的Makefile

源码 CC ? clang CFLAGS -Ofast -Wno-unused-result -Wno-ignored-pragmas -Wno-unknown-attributes LDFLAGS LDLIBS -lm INCLUDES CFLAGS_COND -marchnative# Find nvcc SHELL_UNAME $(shell uname) REMOVE_FILES rm -f OUTPUT_FILE -o $ CUDA_OUTPUT_FILE -o $# N…

内容检索(2024.05.12)

随着创作数量的增加&#xff0c;博客文章所涉及的内容越来越庞杂&#xff0c;为了更为方便地阅读&#xff0c;后续更新发布的文章将陆续在此汇总并附上原文链接&#xff0c;感兴趣的小伙伴们可持续关注文章发布动态&#xff01; 本期更新内容&#xff1a; 1. 信号仿真类话题-…

AWTK 开源串口屏开发(18) - 用 C 语言自定义命令

AWTK-HMI 内置了不少模型&#xff0c;利用这些模型开发应用程序&#xff0c;不需要编写代码即可实现常见的应用。但是&#xff0c;有时候我们需要自定义一些命令&#xff0c;以实现一些特殊的功能。 本文档介绍如何使用 C 语言自定义命令。 1. 实现 hmi_model_cmd_t 接口 1.1…

临近空间相关概念

临近空间概念 距地 20KM-100KM 的临近空间位于内外层空间之中&#xff0c;也称为 超高空、近空间、亚轨道等。 特点就是&#xff1a;纵跨 非电离层和电离层、空气稀薄&#xff0c;存在 臭氧、紫外、辐射等特殊环境 存在 重力波、行星波、大气放电等特殊现象。 临近空间高速飞…

4---自动化构建代码(逻辑梳理,轻松理解)

一、需求引出&#xff1a; 在使用编译器编译代码时&#xff0c;无论我们在一个项目中写了多少个文件(包括头文件、源文件)&#xff0c;我们都可以一键完成编译&#xff0c;编译器会自动处理各个文件之间的包含&#xff0c;调用关系。但是在Linux中&#xff0c;我们在一个目录下…

怎么把学浪课程视频下载到相册

在这个快节奏的学习时代&#xff0c;每一刻的知识获取都显得至关重要。想象一下&#xff0c;在浩瀚如海的学浪app中&#xff0c;你已经找到了那些能够点亮智慧的课程视频&#xff0c;它们不仅充满了启发&#xff0c;还是你求学旅途中的宝贵资源。但是&#xff0c;在网络不稳定或…

【Unity】Unity项目转抖音小游戏(二)云数据库和云函数

业务需求&#xff0c;开始接触一下抖音小游戏相关的内容&#xff0c;开发过程中记录一下流程。 抖音云官方文档&#xff1a;https://developer.open-douyin.com/docs/resource/zh-CN/developer/tools/cloud/develop-guide/cloud-function-debug 1.开通抖音云环境 抖音云地址&a…

2024自动化测试市场分析

大家都说2024年软件测试讲会卷的更厉害,从原来的功能测试到现在自动化测试,那么2024年是否可以学习自动化冲一把,我们先看一下2023年自动化测试在测试行业中的分析: 1.市场需求增长&#xff1a; 随着技术的进步和企业对软件质量的要求日益提高&#xff0c;自动化测试在测试行…

设计模式-结构型-适配器模式-Adapter

地址类 public class Address {public void street() {System.out.println("普通的街道");}public void zip() {System.out.println("普通的邮政编码");}public void city() {System.out.println("普通的城市");} } 荷兰地址类 public class …