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

news2024/9/21 22:41:42

本文涉及知识点

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

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.coloradmin.cn/o/1666344.html

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

相关文章

机器学习入门到放弃2:朴素贝叶斯

1. 算法介绍 1.1 算法定义 朴素贝叶斯分类&#xff08;NBC&#xff09;是以贝叶斯定理为基础并且假设特征条件之间相互独立的方法&#xff0c;先通过已给定的训练集&#xff0c;以特征词之间独立作为前提假设&#xff0c;学习从输入到输出的联合概率分布&#xff0c;再基于学习…

oracle 数据库与服务、实例与SID、表空间、用户与表模式

一、数据库与数据库服务: 概念:就是一个数据库的标识,在安装时就要想好,以后一般不修改,修改起来也麻烦,因为数据库一旦安装,数据库名就写进了控制文件,数据库表,很多地方都会用到这个数据库名。是数据库系统的入口,它会内置一些高级权限的用户如SYS,SYSTEM等。我们…

Xilinx 千兆以太网TEMAC IP核 MDIO 配置及物理接口

基于AXI4-Lite接口可以访问MDIO(Management Data Input/Output)接口&#xff0c;而MDIO接口连接MAC外部的PHY芯片&#xff0c;用户可通过AXI4-Lite接口实现对PHY芯片的配置。 1 MDIO接口简介 开放系统互连模型OSI的最低两层分别是数据链路层和物理层&#xff0c;数据链路层的…

探讨欧盟就人工智能监管达成协议

《人工智能法案》是一项具有里程碑意义的立法&#xff0c;它可以创造一个有利的环境&#xff0c;在这种环境中&#xff0c;人工智能的使用将成为一种更优秀的安全和信任的工具&#xff0c;确保整个欧盟的公共和私人机构利益相关者的参与。 历时3天的“马拉松式”谈判圆满结束&…

数据可视化训练第四天(模拟投掷筛子并且统计频次)

投掷一个筛子 import matplotlib.pyplot as plt from random import randint import numpy as npclass Die:"""模拟投掷筛子"""def __init__(self,num_sides6):self.num_sidesnum_sidesdef roll(self):return randint(1,self.num_sides)num1000…

vi\vim编辑器

root用户&#xff08;超级管理员&#xff09; 无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。 在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root&#xff08;超级管理员&#xff09; root用户拥有最大的系统操作权限&#xff0c;而普通…

论文盲审吐槽多,谁给盲审不负责的老师买单?如何看待浙江大学「一刀切」的研究生学位论文双盲评审制度?

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

二维数组 和 变长数组

在上一期的内容中&#xff0c;为诸君讲解到了一维数组&#xff0c;在一维数组的基础上&#xff0c;C语言中还有着多维数组&#xff0c;其中&#xff0c;比较典型且运用较为广泛的就是我们今天的主角——二维数组 一 . 二维数组的概念 我们把单个或者多个元素组成的数组定义为一…

DI-engine强化学习入门(七)如何自定义神经网络模型

在强化学习中,需要根据决策问题和策略选择合适的神经网络。DI-engine中,神经网络模型可以通过两种方式指定: 使用配置文件中的cfg.policy.model自动生成默认模型。这种方式下,可以在配置文件中指定神经网络的类型(MLP、CNN等)以及超参数(隐层大小、激活函数等),DI-engine会根据…

【漏洞复现】泛微OA E-Cology XmlRpcServlet文件读取漏洞

漏洞描述&#xff1a; 泛微OA E-Cology是一款面向中大型组织的数字化办公产品&#xff0c;它基于全新的设计理念和管理思想&#xff0c;旨在为中大型组织创建一个全新的高效协同办公环境。泛微OA E-Cology XmlRpcServlet存在任意文件读取漏洞&#xff0c;允许未经授权的用户读…

三星硬盘格式化后怎么恢复数据

在数字化时代&#xff0c;硬盘作为数据存储的核心部件&#xff0c;承载着我们的重要文件、照片、视频等资料。然而&#xff0c;不慎的格式化操作可能使我们失去宝贵的数据。面对这样的困境&#xff0c;许多用户可能会感到无助和焦虑。本文旨在为三星硬盘用户提供格式化后的数据…

计算机网络实验2:路由器常用协议配置

实验目的和要求 掌握路由器基本配置原理理解路由器路由算法原理理解路由器路由配置方法实验项目内容 路由器的基本配置 路由器单臂路由配置 路由器静态路由配置 路由器RIP动态路由配置 路由器OSPF动态路由配置实验环境 1. 硬件&#xff1a;PC机&#xff1b; 2. 软…

金三银四面试题(二十六):责任链模式知多少?

什么是责任链模式 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;旨在通过将请求的处理分布在一系列对象上&#xff0c;从而使得多个对象可以尝试处理同一个请求。这些对象被链接成一条链&#xff0c;每个对象都可以对请…

stm32——OLED篇

技术笔记&#xff01; 一、OLED显示屏介绍&#xff08;了解&#xff09; 1. OLED显示屏简介 二、OLED驱动原理&#xff08;熟悉&#xff09; 1. 驱动OLED驱动芯片的步骤 2. SSD1306工作时序 三、OLED驱动芯片简介&#xff08;掌握&#xff09; 1. 常用SSD1306指令 2. …

专业130+总分400+哈尔滨工程大学810信号与系统考研哈工程水声电子信息通信工程,真题,大纲,参考书。

毕业设计刚搞完&#xff0c;总结一下去年考研的复习经历&#xff0c;希望对大家复习有帮助&#xff0c;考研专业课810信号与系统130总分400&#xff0c;如愿上岸哈工程水声。专业课&#xff1a;130 哈工程水声院810专业课信号与系统难度适中&#xff0c;目前数一难度很高&…

【C语言/Python】嵌入式常用数据滤波处理:卡尔曼滤波器的简易实现方式(Kalman Filter)

【C语言/Python】嵌入式常用数据滤波处理&#xff1a;卡尔曼滤波器的简易实现方式&#xff08;Kalman Filter&#xff09; 文章目录 卡尔曼滤波卡尔曼滤波公式卡尔曼滤波数据处理效果C语言的卡尔曼滤波实现附录&#xff1a;压缩字符串、大小端格式转换压缩字符串浮点数压缩Pack…

TCP三次握手四次挥手 UDP

TCP是面向链接的协议&#xff0c;而UDP是无连接的协议 TCP的三次握手 三次传输过程是纯粹的不涉及数据&#xff0c;三次握手的几个数据包中不包含数据内容。它的应用层&#xff0c;数据部分是空的&#xff0c;只是TCP实现会话建立&#xff0c;点到点的连接 TCP的四次挥手 第四…

JVM堆内存分析

jmap工具查看堆内存 jmap:全称JVM Memory Map 是一个可以输出所有内存中对象的工具&#xff0c;可以将JVM中的heap&#xff08;堆&#xff09;&#xff0c;以二进制输出成文本&#xff0c;打印出Java进程对应的内存 找到pid jmap -heap 19792 Attaching to process ID 19792…

贪心算法-----柠檬水找零

今日题目&#xff1a;leetcode860 题目链接&#xff1a;点击跳转题目 分析&#xff1a; 顾客只会给三种面值&#xff1a;5、10、20&#xff0c;先分类讨论 当收到5美元时&#xff1a;不用找零&#xff0c;面值5张数1当收到10美元时&#xff1a;找零5美元&#xff0c;面值5张数…

bevformer详解(1):论文介绍

3D 视觉感知任务,包括基于多摄像头的3D检测和地图分割对于自动驾驶系统至关重要。本文提出了一种名为BEVFormer的新框架,它通过使用空间和时间的Transformer 学习统一的BEV表示来支持多个自动驾驶感知任务。简而言之,BEVFormer通过预定义的网格形式的Bev Query与空间和时间空…