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

2024-05-12 14:44

本文主要是介绍【图论 回溯 广度优先搜索】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++**实现。

这篇关于【图论 回溯 广度优先搜索】126. 单词接龙 II的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/982910

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu1240、hdu1253(三维搜索题)

1、从后往前输入,(x,y,z); 2、从下往上输入,(y , z, x); 3、从左往右输入,(z,x,y); hdu1240代码如下: #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#inc

hdu1180(广搜+优先队列)

此题要求最少到达目标点T的最短时间,所以我选择了广度优先搜索,并且要用到优先队列。 另外此题注意点较多,比如说可以在某个点停留,我wa了好多两次,就是因为忽略了这一点,然后参考了大神的思想,然后经过反复修改才AC的 这是我的代码 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<

poj 3190 优先队列+贪心

题意: 有n头牛,分别给他们挤奶的时间。 然后每头牛挤奶的时候都要在一个stall里面,并且每个stall每次只能占用一头牛。 问最少需要多少个stall,并输出每头牛所在的stall。 e.g 样例: INPUT: 51 102 43 65 84 7 OUTPUT: 412324 HINT: Explanation of the s

poj 2431 poj 3253 优先队列的运用

poj 2431: 题意: 一条路起点为0, 终点为l。 卡车初始时在0点,并且有p升油,假设油箱无限大。 给n个加油站,每个加油站距离终点 l 距离为 x[i],可以加的油量为fuel[i]。 问最少加几次油可以到达终点,若不能到达,输出-1。 解析: 《挑战程序设计竞赛》: “在卡车开往终点的途中,只有在加油站才可以加油。但是,如果认为“在到达加油站i时,就获得了一

hdu 4517 floyd+记忆化搜索

题意: 有n(100)个景点,m(1000)条路,时间限制为t(300),起点s,终点e。 访问每个景点需要时间cost_i,每个景点的访问价值为value_i。 点与点之间行走需要花费的时间为g[ i ] [ j ] 。注意点间可能有多条边。 走到一个点时可以选择访问或者不访问,并且当前点的访问价值应该严格大于前一个访问的点。 现在求,从起点出发,到达终点,在时间限制内,能得到的最大

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

hdu4277搜索

给你n个有长度的线段,问如果用上所有的线段来拼1个三角形,最多能拼出多少种不同的? import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;

从0到1,AI我来了- (7)AI应用-ComfyUI-II(进阶)

上篇comfyUI 入门 ,了解了TA是个啥,这篇,我们通过ComfyUI 及其相关Lora 模型,生成一些更惊艳的图片。这篇主要了解这些内容:         1、哪里获取模型?         2、实践如何画一个美女?         3、附录:               1)相关SD(稳定扩散模型的组成部分)               2)模型放置目录(重要)

POJ2010 贪心优先队列

c头牛,需要选n头(奇数);学校总共有f的资金, 每头牛分数score和学费cost,问合法招生方案中,中间分数(即排名第(n+1)/2)最高的是多少。 n头牛按照先score后cost从小到大排序; 枚举中间score的牛,  预处理左边与右边的最小花费和。 预处理直接优先队列贪心 public class Main {public static voi