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

news2024/11/28 4:05:20

本文涉及知识点

深度优先搜索(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中的节点为终点的路径

mNumschildNums路径
处理根节点{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

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

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

相关文章

3.2-Docker Image概述

常用docker命令&#xff1a; 查看docker image有哪些 docker image ls Image的获取方式

亚马逊云科技产品测评』活动征文|通过使用Amazon Neptune来预测电影类型初体验

文章目录 福利来袭Amazon Neptune什么是图数据库为什么要使用图数据库什么是Amazon NeptuneNeptune 的特点 快速入门环境搭建notebook 图神经网络快速构建加载数据配置端点Gremlin 查询清理 删除环境S3 存储桶删除 授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转…

jstat虚拟机统计信息监控工具

jstat虚拟机统计信息监控工具 1、jstat&#xff08;JVM Statistics Monitorning Tool&#xff09; 用于监控虚拟机各种运行状态信息的命令行工具。 它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据&#xff0c;它是运行期定位虚拟机 性能问题…

Lenovo联想IdeaPad 15 ALC7(82R4)2022款笔记本原装出厂Windows11预装系统镜像

下载链接&#xff1a;https://pan.baidu.com/s/1Pr2G42Sz1LKzgF8OcEBkPQ?pwd5ms2 提取码&#xff1a;5ms2 系统自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;…

centos7.9 postgresql 16.0 源码安装部署

postgresql 16.0 源码安装部署 环境准备 系统主机名IP地址centos7.9postgres192.168.200.56 软件准备 postgresql-16.0.tar.gz https://ftp.postgresql.org/pub/source/v16.0/postgresql-16.0.tar.gz依赖安装 yum -y install systemd-devel readline readline-devel zlib-devel…

探秘Python闭包与作用域

文章目录 闭包的定义与作用LEGB规则nonlocal与global关键字在Python的世界里,理解闭包(Closure)和作用域(Scope)是提升编程技巧和深度的一大步。这篇文章将带你深入了解闭包的神秘面纱,掌握LEGB规则,并使用nonlocal与global关键字来巧妙控制变量作用域。 闭包的定义与作…

MySQL中的刷脏机制详解

名词解释 脏页&#xff1a;当内存数据页跟磁盘数据页内容不一致的时候&#xff0c;我们称这个内存页为“脏页”。 干净页&#xff1a;内存数据写入到磁盘后&#xff0c;内存和磁盘上的数据页的内容就一致了&#xff0c;称为“干净页”。 LSN&#xff1a;称为日志的逻辑序列号(l…

万物社用户运营工具:无代码开发下的电商平台和CRM集成

简介&#xff1a;万物社与集简云的引领式连接 万物社&#xff0c;隶属于厦门头号云信息科技有限公司&#xff0c;是一家专注于互联网和相关服务的企业。在日常的业务运营中&#xff0c;万物社通过与集简云的无代码集成&#xff0c;实现了业务流程的自动化和智能化&#xff0c;…

学习笔记4——JVM运行时数据区梳理

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/contents/3/192489.html 类装载器classLoader&#xff1a; 将本地的字节码文件.class 加载到内存方法区中成为元数据模板&#xff08;两个class对象是否为同一个类要求&#xff1a;完整…

对话句子互动创始人李佳芮 | AIGC结合私域运营影响不可估量

“ 创业最核心的就是耐心” 口述 | 李佳芮 整理 | 小白&云舒 出品&#xff5c;极新 极新请文心一言分析了私域流量运营和chatbot当下的发展背景&#xff0c;它给出了以下答案&#xff1a; 1. 移动设备普及和网络速度提升&#xff1a;随着智能手机和移动互联网的普及&…

UE5.3实现1秒12帧风格的动画抽帧效果

现今一些卡通风格游戏会刻意模仿早期动画1秒12帧的播放效果&#xff0c;以营造较强的风格化体验&#xff0c;博主在UE5中实现了一下&#xff08;左侧正常动画&#xff0c;右侧抽帧动画&#xff09;&#xff1a; 我们可以通过在UE中对导入设置进行一些修改&#xff0c;达到不改…

Android UI 开发·界面布局开发·案例分析

目录 ​编辑 1. 线性布局&#xff08;LinearLayout&#xff09; 2. 相对布局&#xff08;RelativeLayout&#xff09; 3. 表格布局&#xff08;TableLayout&#xff09; 4. 帧布局&#xff08;FrameLayout&#xff09; 5. 网格布局&#xff08;GridLayout&#xff0…

AI:68-基于深度学习的身份证号码识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

C语言编写一个程序采集招聘信息

因为在这里无法详细解释每行代码和步骤。但是&#xff0c;我可以给大家一个使用Python和requests库编写的简单爬虫程序的例子&#xff0c;它可以从网站上获取招聘信息。你可以根据这个例子&#xff0c;将其改写为使用C语言编写的爬虫程序。 import requests# 指定爬虫IP信息 pr…

二叉树的OJ题——C++

一、根据二叉树创建字符串 题目链接&#xff1a; 606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 前序遍历二叉树&#xff0c;并且将结果存入到一个string中&#xff0c;并且要使用括号去分割和表示每个节点和子树之间的关系&…

大漠插件(二、Qt使用插件时注意事项)

本章目的 在上篇已经注册完毕大漠&#xff0c;那么怎么使用大漠来制作脚本&#xff0c;我选择了我最熟悉的Qt来开发&#xff0c;毕竟只是小软件&#xff0c;用脚本或者c都差不了多少。本章就是开发途中的一些坑。 本人开发环境是 win11 64、Qt 5.15.2安装了5.10.0的msvc2015 32…

接口测试工具的实验,Postman、Swagger、knife4j(黑马头条)

一、Postman 最常用的接口测试软件&#xff0c;需要注意点&#xff1a;在进行post请求时&#xff0c;需要选择JSON形式发送 输入JSON字符串&#xff0c;比如&#xff1a; {"maxBehotTime": "2021-04-19 00:19:09","minBehotTime": "2021-…

支持存档的书签服务LinkWarden

什么是 LinkWarden &#xff1f; Linkwarden 是一个自托管、开源协作书签管理器&#xff0c;用于收集、组织和存档网页。目标是将您在网络上找到的有用网页和文章组织到一个地方&#xff0c;并且由于有用的网页可能会消失&#xff08;参见链接失效的必然性&#xff09;&#xf…

读者自荐的 4 个 GitHub 项目

本期推荐的 4 个开源项目&#xff0c;为读者在开源项目 Awesome-GitHub-Repo 的评论区自推的, 如果你开源了不错的项目&#xff0c;想让大家看到&#xff0c;也可以去 Awesome-GitHub-Repo 进行投稿。 本期推荐开源项目目录&#xff1a; 1. DB-GPT 2. 定制中国传统节日头像 3. …

闪客网盘系统源码,已测试对接腾讯COS及本地和支付(支持限速+按时收费+文件分享+可对接易支付)- 修复版

正文概述 资源入口 支持对文件下载限速 对接易支付 推广赚钱啥的功能 源码非常的好 支持腾讯cos 阿里云cos 本地储存 远程存储 源码仅支持服务器搭建 php7.2 伪静态thinkphp 运行目录public 导入数据库 修改config目录下的database.php数据库信息 后台地址&#xff1a; 域名/ad…