【深度优先搜索】【树】【状态压缩】2791. 树中可以形成回文的路径数

news2025/1/11 6:51:25

作者推荐

【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II

本文涉及知识点

深度优先搜索 树 图论 状态压缩

LeetCode:2791. 树中可以形成回文的路径数

给你一棵 树(即,一个连通、无向且无环的图),根 节点为 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 仅由小写英文字母组成

深度优先搜索

树的路径一定是 起点 → \rightarrow 公共祖先 → \rightarrow 终点,特例是起点(或终点)就是公共祖先。
且从 u 到 v 的路径上分配的字符可以 重新排列 形成 回文    ⟺    \iff 各字符的数量只有1个是奇数或全部是偶数
状态压缩: mask &(1 << i ) 表示 ‘a’+i 出现的次数是奇数。

深度优先搜索的状态

暴力的办法是枚举左支和右支的状态,空间复杂度(226) 已经在超内存的边缘了。
状态优化

在这里插入图片描述
如果node1到node2的路径能构成回文,那node1 → \rightarrow root → \rightarrow node2 也能构成回文。起点(或终点)就是公共祖先 也符合。
单节点要排除,因为不符合v<v。

深度优先搜索的转移方程

vCnt[mask] 记录 各节点到root(0)的mask。
node1的压缩状态为:mask
cnt += vCnt[mask]-1
cnt += ∑ j : 0 25 v C n t [ m a s k ( 1 < < j ) ] \sum_{j:0}^{25}vCnt[mask^(1<<j)] j:025vCnt[mask(1<<j)]

深度优先搜索的返回值

cnt/2。

代码

核心代码

class Solution {
public:
	long long countPalindromePaths(vector<int>& parent, string s) {
		m_s = s; 
		vector<vector<int>> vNeiBo(parent.size());
		for (int i = 1; i < parent.size(); i++)
		{
			vNeiBo[parent[i]].emplace_back(i);
		}
		DFS(vNeiBo, 0, -1, 0);
		long long llRet = 0;
		for (const auto& [mask, cnt] : m_mMaskCount)
		{
			llRet += cnt*(cnt - 1);
			for (int j = 0; j < 26; j++)
			{
				const int iNewMask = mask ^ (1 << j);
				if (m_mMaskCount.count(iNewMask))
				{
					llRet += cnt*m_mMaskCount[iNewMask];
				}
			}
		}
		return llRet / 2;
	}
	void DFS(vector<vector<int>>& neiBo, int cur, int par, int parMask)
	{
		parMask ^= (1 << (m_s[cur]-'a'));
		m_mMaskCount[parMask]++;
		for (const auto& next : neiBo[cur])
		{
			if (next == par)
			{
				continue;
			}
			DFS(neiBo, next, cur, parMask);
		}
	}
	unordered_map<int, long long> m_mMaskCount;
	string m_s;
};

测试用例

template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{
	assert(t1 == t2);
}

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]);
	}

}

int main()
{	
	vector<int> parent;
	string s;
	{
		Solution sln;
		parent = { -1, 0, 0, 1, 1, 2 }, s = "acaabc";
		auto res = sln.countPalindromePaths(parent, s);
		Assert(res,8);
	}

	{
		Solution sln;
		parent = { -1, 0, 0, 0, 0 }, s = "aaaaa";
		auto res = sln.countPalindromePaths(parent, s);
		Assert(res, 10);
	}
	
}

2023年11月版

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;
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> 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++**实现。

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

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

相关文章

【Hudi】Upsert原理

17张图带你彻底理解Hudi Upsert原理 1.开始提交&#xff1a;判断上次任务是否失败&#xff0c;如果失败会触发回滚操作。然后会根据当前时间生成一个事务开始的请求标识元数据。2.构造HoodieRecord Rdd对象&#xff1a;Hudi 会根据元数据信息构造HoodieRecord Rdd 对象&#xf…

多维时序 | Matlab实现BiLSTM-MATT双向长短期记忆神经网络融合多头注意力多变量时间序列预测模型

多维时序 | Matlab实现BiLSTM-MATT双向长短期记忆神经网络融合多头注意力多变量时间序列预测模型 目录 多维时序 | Matlab实现BiLSTM-MATT双向长短期记忆神经网络融合多头注意力多变量时间序列预测模型预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.多维时序 | Matlab…

ClickHouse快速上手

简介 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS) 官网(https://clickhouse.com/docs/zh)给出的定义&#xff0c;其实没看懂 特性 ClickHouse支持一种基于SQL的声明式查询语言&#xff0c;它在许多情况下与ANSI SQL标准相同。使用时和MySQL有点相似&#…

vue 实现docx文件在线预览

预览请求步骤&#xff1a; 准备一个需要预览的文件地址【如:https://usdoc.cn/vw/文件模板.docx】GET 参数到请求地址将拼接好的地址放在浏览器中&#xff0c;开始预览 请求地址&#xff1a;https://vw.usdoc.cn/ 请求方式&#xff1a;GET 请求参数: 字段名称字段类型必填参数…

C++ bfs 的状态表示(六十二)【第九篇】

今天我们来学习一下bfs的复杂状态表示 1.bfs状态表示 无论是深度优先搜索还是广度优先搜索&#xff0c;搜索的过程均会建立一棵 搜索树&#xff0c;搜索树上的每一个结点都是一个 状态&#xff0c;而搜索的过程又可以看作是 状态的转移。 对于 BFS&#xff0c;搜索过程中产生…

测试C#调用Emgucv读取并显示视频文件

微信公众号“CSharp编程大全”的文章《C# 视频播放》介绍了基于emgucv读取视频文件并播放的用法&#xff08;百度文章名没有找到对象的文章地址&#xff0c;有兴趣的可以在微信中搜索该公众号并查看文章具体内容&#xff09;&#xff0c;本文学习并测试Emgucv播放视频文件的基本…

“成像光谱遥感技术中的AI革命:ChatGPT应用指南“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本课程重点介绍ChatGPT在遥感中的应用&#xff0c;人工智…

ChatGPT在数据分析面试阶段的应用

ChatGPT在数据分析面试阶段的应用 ​ ChatGPT不仅在岗位了解阶段和学习阶段有用&#xff0c;在面试阶段也很有用。我们可以利用ChatGPT确定应聘目标、优化简历、准备面试。 1.1 确定应聘目标 ​ 在准备应聘之前&#xff0c;可以利用ChatGPT解读招聘信息、了解目标公司的福利…

Atcoder ABC340 E - Mancala 2

Mancala 2&#xff08;曼卡拉 2&#xff09; 时间限制&#xff1a;2s 内存限制&#xff1a;1024MB 【原题地址】 所有图片源自Atcoder&#xff0c;题目译文源自脚本Atcoder Better! 点击此处跳转至原题 【问题描述】 【输入格式】 【输出格式】 【样例1】 【样例输入1】 …

架构(十四)动态Groovy脚本

一、引言 最近作者的平台项目需要实现前端输入脚本&#xff0c;后端在用户设置好的一些情况下运行这段脚本。后端是java&#xff0c;所以我们采用Groovy脚本。 所以要实现的功能就是动态的Groovy脚本&#xff01; 二、Groovy介绍 了解groovy和python的就可以直接到第三章了 2…

【git】提交信息写错了,使用 amend 或者 reset 修改最近一次的提交信息 ,修改上上次/以前的提交信息

如果你的提交信息写错了&#xff0c;比如下面&#xff0c;你想修改【初始化项目】这5个字 修改最近一次的提交新的两个办法 &#xff08;1&#xff09;使用 reset 把这个提交重置&#xff0c;然后重新提交&#xff0c;reset 的使用方法请参考这篇文章。但是 reset 这种方法只能…

django请求生命周期流程图,路由匹配,路由有名无名反向解析,路由分发,名称空间

django请求生命周期流程图 浏览器发起请求。 先经过网关接口&#xff0c;Django自带的是wsgiref&#xff0c;请求来的时候解析封装&#xff0c;响应走的时候打包处理&#xff0c;这个wsgiref模块本身能够支持的并发量很少&#xff0c;最多1000左右&#xff0c;上线之后会换成u…

Redis(十四)双写一致性工程案例

文章目录 问题概述canal功能安装部署mysql配置canal服务端canal客户端&#xff08;Java程序&#xff09; 问题概述 canal https://github.com/alibaba/canal 功能 数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、倒排索引等)业务 cache 刷新带业务逻辑的增量数据…

springboot当中使用EMQX(MQTT协议)

本篇博客主要围绕EMQX是什么&#xff1f;、能干什么&#xff1f;、怎么用&#xff1f; 三点来进行整理。 1、MQTT协议 1.1、MQTT简介 在了解EMQX前首先了解一下MQTT协议&#xff0c;MQTT 全称为 Message Queuing Telemetry Transport&#xff08;消息队列遥测传输&#xff0…

IDEA查询对应功能的快捷键

首先要知道快捷键的key叫什么&#xff0c;然后通过key来找到对应的快捷键 比如下面这个查找删除导入未使用的类 跳转 或者安装对应插件

多维时序 | Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、BiLSTM的多变量时间序列预测

多维时序 | Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、BiLSTM的多变量时间序列预测 目录 多维时序 | Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、BiLSTM的多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、…

【Docker】docker常用命令

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、镜像仓库架构图 二、docker仓库命令 1、登录仓库docker login 2、拉取镜像docker pull 3、推送镜像docker pus…

TOUCHDESIGNER(1-2) 元件

元件来了 一级 二级 三级!!!

Git 客户端可视化工具tortoisegit

Git 使用教程 git一点通 (kdocs.cn) 二、Git 客户端可视化工具-推荐 1.常用工具 tortoisegit 官网 https://tortoisegit.org/ 推荐 sourcetree 官网 https://www.sourcetreeapp.com/ 2.tortoisegit安装 2.1 下载安装包 2.2 下载语言包 2.3 安装 2.4 安装语言包 5.使用 5.1 新建…

说一下JVM类加载机制?

Java中的所有类&#xff0c;都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类&#xff0c;而它的工作就是把class文件从硬盘读取到内存中。 在写程序的时候&#xff0c;我们几乎不需要关心类的加载&#xff0c;因为这些都是隐式装载的&#xff0c;除非我们有特殊…