首页 > 编程语言 >【C++ DFS 图论】1519. 子树中标签相同的节点数|1808

【C++ DFS 图论】1519. 子树中标签相同的节点数|1808

时间:2024-12-07 17:29:04浏览次数:6  
标签:auto edges labels 子树中 DFS 1808 vector leves 节点

本文涉及知识点

C++DFS
C++图论

LeetCode1519. 子树中标签相同的节点数

给你一棵树(即,一个连通的无环无向图),这棵树由编号从 0 到 n - 1 的 n 个节点组成,且恰好有 n - 1 条 edges 。树的根节点为节点 0 ,树上的每一个节点都有一个标签,也就是字符串 labels 中的一个小写字符(编号为 i 的 节点的标签就是 labels[i] )
边数组 edges 以 edges[i] = [ai, bi] 的形式给出,该格式表示节点 ai 和 bi 之间存在一条边。
返回一个大小为 n 的数组,其中 ans[i] 表示第 i 个节点的子树中与节点 i 标签相同的节点数。
树 T 中的子树是由 T 中的某个节点及其所有后代节点组成的树。
示例 1:
在这里插入图片描述

输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], labels = “abaedcd”
输出:[2,1,1,1,1,1,1]
解释:节点 0 的标签为 ‘a’ ,以 ‘a’ 为根节点的子树中,节点 2 的标签也是 ‘a’ ,因此答案为 2 。注意树中的每个节点都是这棵子树的一部分。
节点 1 的标签为 ‘b’ ,节点 1 的子树包含节点 1、4 和 5,但是节点 4、5 的标签与节点 1 不同,故而答案为 1(即,该节点本身)。
示例 2:
在这里插入图片描述

输入:n = 4, edges = [[0,1],[1,2],[0,3]], labels = “bbbb”
输出:[4,2,1,1]
解释:节点 2 的子树中只有节点 2 ,所以答案为 1 。
节点 3 的子树中只有节点 3 ,所以答案为 1 。
节点 1 的子树中包含节点 1 和 2 ,标签都是 ‘b’ ,因此答案为 2 。
节点 0 的子树中包含节点 0、1、2 和 3,标签都是 ‘b’,因此答案为 4 。
示例 3:

在这里插入图片描述

输入:n = 5, edges = [[0,1],[0,2],[1,3],[0,4]], labels = “aabab”
输出:[3,2,1,1,1]

提示:
1 <= n <= 105
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
labels.length == n
labels 仅由小写英文字母组成

后续DFS

m_ans[i][j] 记录以节点i为根的子树,包括字符’a’+j的数量。
显然:m_ans[i][j] = labels[i] == ‘a’+j。
然后累加: m_ans[next][j],next是子节点。

封装类

用DFS 或BFS 获取各节点层次。然后将层次转成 leveNodes, leves[i] 包括所有层次为i的节点。
层次大的节点先处理。

代码

核心代码

class CNeiBo
{
public:	
	static vector<vector<int>> Two(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) 
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}	
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}	
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

class CBFSLeve {
public :
	static vector<int> Leve(const vector<vector<int>>& neiBo, vector<int> start) {
		vector<int> leves(neiBo.size(), -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			for (const auto& next : neiBo[start[i]]) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	template<class NextFun>
	static vector<int> Leve(int N,NextFun nextFun, vector<int> start) {
		vector<int> leves(N, -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			auto nexts = nextFun(start[i]);
			for (const auto& next : nexts) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	static vector<vector<int>> LeveNodes(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> ret(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			ret[leves[i]].emplace_back(i);
		}
		return ret;
	};
};

class Solution {
		public:
			vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
				auto neiBo = CNeiBo::Two(n, edges,false);
				auto leves = CBFSLeve::Leve(neiBo, { 0 });	
				auto leveNodes = CBFSLeve::LeveNodes(leves);
				vector<vector<int>> cnt(n, vector<int>(26));
				vector<int> ans(n);
				for (auto it = leveNodes.rbegin(); it != leveNodes.rend(); ++it) {
					for (const auto& cur : *it) {
						cnt[cur][labels[cur] - 'a']++;						
						for (const auto& next : neiBo[cur]) {
							if (leves[next] < leves[cur]) { continue; }
							for (int j = 0; j < 26; j++) {
								cnt[cur][j] += cnt[next][j];
							}
						}
						ans[cur] = cnt[cur][labels[cur] - 'a'];
					}
				}
				return ans;
			}
		};

单元测试

int n;
		vector<vector<int>> edges;
		string labels;
		TEST_METHOD(TestMethod11)
		{
			n = 7, edges = { {0,1},{0,2},{1,4},{1,5},{2,3},{2,6} }, labels = "abaedcd";
			auto res = Solution().countSubTrees(n, edges, labels);
			AssertEx({ 2,1,1,1,1,1,1 }, res);
		}
		TEST_METHOD(TestMethod12)
		{
			n = 4, edges = { {0,1},{1,2},{0,3} }, labels = "bbbb";
			auto res = Solution().countSubTrees(n, edges, labels);
			AssertEx({ 4,2,1,1 }, res);
		}
		TEST_METHOD(TestMethod13)
		{
			n = 5, edges = { {0,1},{0,2},{1,3},{0,4} }, labels = "aabab";
			auto res = Solution().countSubTrees(n, edges, labels);
			AssertEx({3,2,1,1,1 }, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

标签:auto,edges,labels,子树中,DFS,1808,vector,leves,节点
From: https://blog.csdn.net/he_zhidan/article/details/143108705

相关文章

  • 信奥赛CSP-J复赛集训(dfs专题)(11):洛谷P1036:[NOIP2002 普及组] 选数
    信奥赛CSP-J复赛集训(dfs专题-刷题题单及题解)(11):洛谷P1036:[NOIP2002普及组]选数题目描述已知nnn个整数x......
  • 信奥赛CSP-J复赛集训(dfs专题)(12):洛谷P2404:自然数的拆分问题
    信奥赛CSP-J复赛集训(dfs专题-刷题题单及题解)(12):洛谷P2404:自然数的拆分问题题目描述任何一个大于111的自然数n......
  • 天梯赛练习集 L2-048 寻宝图 DFS
    思路:dfs,从一块岛屿出发,搜索与之连通的所有岛屿块标记为0,计数器+1,过程中用一个变量flag标记有没有宝藏。反思:如果用二维int数组直接存储会爆空间,所以用一维字符串数组。#include<bits/stdc++.h>usingnamespacestd;vector<string>vc;strings;intn,m,cot=0,flag=0,sum=0;......
  • 电商项目--分布式文件存储FastDFS搭建
    一、FastDFS环境搭建我们使用Docker搭建FastDFS的开发环境(1)拉取镜像dockerpullmorunchang/fastdfs (2)运行trackerdockerrun-d--nametracker--net=hostmorunchang/fastdfsshtracker.sh(3)运行storagedockerrun-d--namestorage--net=host-eTRACKER_......
  • 电商项目--分布式文件存储FastDFS简介
    对于大型互联网类型项目,其内部存在非常多的文件,会存在图片文档报表等文件。采用传统方式存储在机器磁盘上,其容量无法支撑这些文件,需要考虑分布式文件系统。一、FastDFS简介FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同......
  • 【LeetCode:51. N 皇后 + DFS】
    ......
  • 1030 Travel Plan(dijsktra + dfs/bfs + 回溯)
     题面意思比较清晰,就是优先最短路,同距离取最小花费。1#include<bits/stdc++.h>2usingnamespacestd;3intn,m,s,d;4typedefpair<int,int>pii;5vector<pii>graph[505];6set<pii>min_heap;7intcost[505][505]={0};8vector<bool>v......
  • ICPC2022济南站C. DFS Order 2 题解 回滚背包
    题目链接:https://www.luogu.com.cn/problem/P9669题目大意:给你一棵包含\(n\)个节点的有根树。节点编号从\(1\)到\(n\),节点\(1\)是根节点。从节点\(1\)出发对整棵树进行深度优先遍历,会得到很多不同的DFS序。解题思路:基本上和9981day大佬的题解一模一样差不多。......
  • 2024算法基础公选课练习五(DFS2)
    一、前言因为此次题目较多,我也不想分成两篇博客来发,我就直接给代码了,如果题目有需要强调的地方再特殊说明二、题目总览三、具体题目3.1问题A:勘探油田我的代码8方向的floodfill模型#include<bits/stdc++.h>usingi64=longlong;constexprintN=1e2+5,M=......
  • DFS 基础
    判父亲法voiddfs(intu){ //保存父亲 //标记父亲 if()//如果父亲是答案就输出(判父亲法) { //输出答案 } else//如果父亲不是答案,继续从下一代找 { for(inti=1;i<=N;i++)//枚举第u代的所有孩子的i { //生成孩子 if()//判断孩子的合法性 { dfs(i......