C++深度优先搜索(DFS)算法的应用:树中可以形成回文的路径数

这篇具有很好参考价值的文章主要介绍了C++深度优先搜索(DFS)算法的应用:树中可以形成回文的路径数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文涉及知识点

深度优先搜索(DFS) 状态压缩

题目

给你一棵 树(即,一个连通、无向且无环的图),根 节点为 0 ,由编号从 0 到 n - 1 的 n 个节点组成。这棵树用一个长度为 n 、下标从 0 开始的数组 parent 表示,其中 parent[i] 为节点 i 的父节点,由于节点 0 为根节点,所以 parent[0] == -1 。
另给你一个长度为 n 的字符串 s ,其中 s[i] 是分配给 i 和 parent[i] 之间的边的字符。s[0] 可以忽略。
找出满足 u < v ,且从 u 到 v 的路径上分配的字符可以 重新排列 形成 回文 的所有节点对 (u, v) ,并返回节点对的数目。
如果一个字符串正着读和反着读都相同,那么这个字符串就是一个 回文 。
示例 1:
输入:parent = [-1,0,0,1,1,2], s = “acaabc”
输出:8
解释:符合题目要求的节点对分别是:

  • (0,1)、(0,2)、(1,3)、(1,4) 和 (2,5) ,路径上只有一个字符,满足回文定义。
  • (2,3),路径上字符形成的字符串是 “aca” ,满足回文定义。
  • (1,5),路径上字符形成的字符串是 “cac” ,满足回文定义。
  • (3,5),路径上字符形成的字符串是 “acac” ,可以重排形成回文 “acca” 。
    示例 2:
    输入:parent = [-1,0,0,0,0], s = “aaaaa”
    输出:10
    解释:任何满足 u < v 的节点对 (u,v) 都符合题目要求。
    参数提示
    n == parent.length == s.length
    1 <= n <= 105
    对于所有 i >= 1 ,0 <= parent[i] <= n - 1 均成立
    parent[0] == -1
    parent 表示一棵有效的树
    s 仅由小写英文字母组成

解法一稍稍超时,通过不了

分析

状态压缩

排序后能构成回文,那只有两种可能,一:所有字符数量都为偶数。二,有一个字符数量为奇数,其余全部是偶数。可以用二进制状态压缩,每个二进制位表示某个字符是否为偶数。1表示z是奇数数量,2表示y是奇数数量,3表示yz都是奇数数量。

异或(^)

增加一个字符可以用异或操作,由于异或的逆操作就是自己,所以删除字符也用异或。

难理解的地方

mNums记录以下路径:

起点和终点都是cur的路径
起点是cur,终点是已处理子树的任意节点

childNums:记录以child为起点,以child为根节点的子树的任意节点为终点的路径。

下面以{-1,0,0}来说明,由于起点是固定的,所以下表只记录终点。路径指的是:以child子树中的节点为起点,以mNums中的节点为终点的路径

mNums childNums 路径
处理根节点 {0} {}
处理节点1 {0} {1} {0,1}
处理节点2 {0,1} {2} {0,2},{1,2}
{0,1,2}

总结:第四列的路径,就是mNums 和childNums 各取一个节点的两两组合。

注意
一个节点没有字符,所以不是合法路径。

ChangeNum

不要枚举mNums 和childNums ,枚举其中的一个和27种合法可能。

核心代码

class Solution {
public:
long long countPalindromePaths(vector& parent, string s) {
m_c = parent.size();
m_str = s;
m_vNeiBo.assign(m_c, vector());
for (int i = 0; i < 26; i++)
{
m_iVilidMask[i] = 1 << i;
}
m_llRet = 0;
int iRoot = -1;
for (int i = 0; i < m_c; i++)
{
if (-1 == parent[i])
{
iRoot = i;
}
else
{
m_vNeiBo[parent[i]].emplace_back(i);
}
}
std::unordered_map<int, int> mNums;
dfs(iRoot, mNums);
return m_llRet ;
}
void dfs(int cur, std::unordered_map<int, int>& mNums)
{
const int curMask = 1 << (m_str[cur] - ‘a’);
mNums[curMask]++;
for (const auto& child : m_vNeiBo[cur])
{
std::unordered_map<int, int> childNums;
dfs(child, childNums);
ChangeNum(mNums, childNums,curMask);
for (const auto& it : childNums)
{
mNums[it.first ^ curMask] += it.second;
}
}
}
void ChangeNum(const std::unordered_map<int, int>& mNums, const std::unordered_map<int, int>& childNums, const int curMask )
{
for (const auto& it : childNums)
{
for (int i = 0; i < 27; i++)
{
const int iNeedMask = it.first ^ m_iVilidMask[i] ^ curMask;
if (mNums.count(iNeedMask))
{
m_llRet += (long long)it.second * mNums.find(iNeedMask)->second;
}
}
}
}
//状态压缩 1表示z是奇数数量,2表示y是奇数数量,3表示yz都是奇数数量
int m_iVilidMask[27] = { 0 };//记录所有字符都是偶数和只有一个字符是奇数
vector<vector> m_vNeiBo;
//vector m_vNums;
int m_c;
long long m_llRet = 0;//不包括单节点的合法路径数
string m_str;
};

测试用例

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}

int main()
{
Solution slu;
vector parent;
long long res;
string s;
parent = { -1 };
s = “a”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 0LL);
parent = { -1,0 };
s = “aa”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 1LL);
parent = { -1,0,1 };
s = “aaa”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 3LL);
parent = { -1,0,0 };
s = “aaa”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 3LL);
parent = { -1,0,0 };
s = “aba”;
res = slu.countPalindromePaths(parent, s);
Assert(res,2LL);
parent = { -1,0,0 };
s = “baa”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 3LL);
parent = { -1,0,0,1,1,2 };
s = “acaabc”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 8LL);
parent = { -1, 0, 0, 0, 0 };
s = “aaaaa”;
res = slu.countPalindromePaths(parent, s);
Assert(res, 10LL);

//CConsole::Out(res);

}

解法二

分析

假定节点A,B的公共最近祖先是C,那么A到B的路径为:A->C->B和路径A->0->B的 字符数量的奇偶性相同。A->0可以拆分成A->C->0 ,0->B可以拆分成0->C->B。0到C和C到0抵消了。
### 时间复杂度
o(27n)。n是节点数量,27是合法掩码的数量。

代码

class Solution{
public:
	long long countPalindromePaths(vector<int>&parent, string s) {
		m_c = parent.size();
		m_str = s;
		m_vNeiBo.assign(m_c, vector<int>());
		for (int i = 0; i < 26; i++)
		{
			m_iVilidMask[i] = 1 << i;
		}
		m_llRet = 0;
		m_mMaskNums.clear();
		int iRoot = -1;
		for (int i = 0; i < m_c; i++)
		{
			if (-1 == parent[i])
			{
				iRoot = i;
			}
			else
			{
				m_vNeiBo[parent[i]].emplace_back(i);
			}
		}
	
		dfs(iRoot,0);
		return m_llRet;
	}
	void dfs(int cur,int iMask)
	{
		const int curMask = iMask ^ ( 1 << (m_str[cur] - 'a'));
		for (int i = 0; i < 27; i++)
		{
			const int iNeedMask = m_iVilidMask[i] ^ curMask;
			if (m_mMaskNums.count(iNeedMask))
			{
				m_llRet += m_mMaskNums[iNeedMask];
			}
		}
		m_mMaskNums[curMask]++;
		for (const auto& child : m_vNeiBo[cur])
		{
			dfs(child, curMask);
		}
	}
	int m_iVilidMask[27] = { 0 };//记录所有字符都是偶数和只有一个字符是奇数
	vector<vector<int>> m_vNeiBo;
	std::unordered_map<int,int> m_mMaskNums;
	int m_c;
	long long m_llRet = 0;//不包括单节点的合法路径数
	string m_str;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++深度优先搜索(DFS)算法的应用:树中可以形成回文的路径数,数据结构与算法,# 算法题,深度优先,算法,c++,状态压缩,树,路径数,回文文章来源地址https://www.toymoban.com/news/detail-764638.html

到了这里,关于C++深度优先搜索(DFS)算法的应用:树中可以形成回文的路径数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • c++深度优先搜索DFS

    目录 介绍 实现过程 模板 例题详解 1.枚举排列 2.迷宫寻路 3.八皇后 剪枝与优化 作业 今天我们来学习一个极其重要的算法:深度优先搜索。 深度优先搜索,又叫DFS,是遍历图或者数的一种算法,本质就是递归。具体方法:先以一个节点为起点,向一个方向扩展,再以新的节

    2024年01月16日
    浏览(26)
  • 深度优先搜索(DFS)算法

    目录 算法思想 时间复杂度和空间复杂度 算法实现 算法优缺点 应用领域 深度优先搜索(DFS)算法的思想是从图的某个起始顶点开始,沿着一条路径尽可能深入地访问图中的所有顶点,直到不能继续为止,然后返回并探索其他路径。具体而言,DFS算法使用栈数据结构来实现,

    2024年02月05日
    浏览(29)
  • 深度优先搜索(DFS)(算法笔记)

    本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。 深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法,总是以“深度”作为前进的。实现方式是有很多,最

    2024年02月08日
    浏览(33)
  • C++ 更多的DFS深度优先搜索...

    目录 DFS模版 剪枝 DFS的两种状态 使用全局变量存储 使用函数参数存储传递 众所周知,DFS是一种省督有限搜索,可以想象成一棵树根节点K开始递归到最深层的节点,通常用来枚举符合题目的所有可能情况或者个数。 话说,这个代码和全排列有什么不同吗哈哈哈哈哈 剪枝时

    2024年02月13日
    浏览(47)
  • [算法日志]图论: 深度优先搜索(DFS)

    ​ 深度优先搜索算法是一种遍历图这种数据结构的算法策略,其中心思想是朝图节点的一个方向不断跳转,当该节点无下一个节点或所有方向都遍历完时,便回溯朝上一个节点的另一个方向继续遍历。这种搜索策略与回溯法有异曲同工之妙。 正因为和回溯法有相似之处,所

    2024年02月03日
    浏览(41)
  • 【算法详解 | DFS算法】深度优先搜索解走迷宫问题 | 深度优先图遍历

    by.Qin3Yu 本文需要读者掌握 结构体 和 栈 的操作基础,完整代码将在文章末尾展示。 特别声明:本文为了尽可能使用简单描述,以求简单明了,可能部分专有名词定义不准确。 栈相关操作可以参考我的往期博文: 【C++数据结构 | 栈速通】使用栈完成十进制数转二四八进制数

    2024年02月03日
    浏览(39)
  • 第一周算法训练(dfs)(深度优先搜索算法)

    dfs: 深度优先搜索算法 ,是一种用于遍历或 搜索树或图的算法 .沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被

    2024年02月20日
    浏览(36)
  • Python 算法基础篇:深度优先搜索( DFS )和广度优先搜索( BFS )

    深度优先搜索( DFS )和广度优先搜索( BFS )是两种常用的图遍历算法,用于在图中搜索目标节点或遍历图的所有节点。本篇博客将介绍 DFS 和 BFS 算法的基本概念,并通过实例代码演示它们的应用。 😃😄 ❤️ ❤️ ❤️ 深度优先搜索( DFS )是一种用于遍历或搜索图或树

    2024年02月07日
    浏览(47)
  • 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)

    从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次。(连通图与非连通图) 1、访问指定的起始顶点; 2、若当前访问的顶点的邻接顶点有未被访问的,则任选一个访问之;反之,退回到最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕; 3、若

    2024年01月17日
    浏览(33)
  • 【数据结构与算法】搜索算法(深度优先搜索 DFS和广度优先搜索 BFS)以及典型算法例题

    【数据结构与算法】系列文章链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题) 【数据结构与算法】系列文章链接: 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题 【数据结构与算法】系列文章链

    2024年04月13日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包