闻缺陷则喜何志丹

闻缺陷则喜何志丹

本文涉及知识点

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:
【C++ DFS 图论】1519. 子树中标签相同的节点数|1808-LMLPHP

输入: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:
【C++ DFS 图论】1519. 子树中标签相同的节点数|1808-LMLPHP

输入: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:

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

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

提示:
1 <= n <= 10
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);
		}

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

扩展阅读

视频课程

先学简单的课程,请移步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++**实现。

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

12-07 18:54