leetCode 面试题17.07

目录

一、题目描述

二、解题思路

三、代码实现


一、题目描述

每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,则 John 与 Johnny 也相同,即它们有传递和对称性。

在结果列表中,选择字典序最小的名字作为真实名字。

示例:

输入:names = ["John(15)","Jon(12)","Chris(13)","Kris(4)","Christopher(19)"], synonyms = ["(Jon,John)","(John,Johnny)","(Chris,Kris)","(Chris,Christopher)"]
输出:["John(27)","Chris(36)"]

提示:

  • names.length <= 100000

二、解题思路

本质上同名的两个名字之间可以连一条线,每一个节点有权值。所有本质相同的名字之间都是可以连通的,所以这道题也是一道求无向图的连通分量的问题。
还是老三样 :dfs,BFS和并查集。
深度优先搜索的次数和广度优先搜索的次数即为连通块数;并查集最后的结合数即为连通块数。
使用并查集时,题目要求选择字典序最下的名字作为真实名字,在集合合并的时候选择字典序小的作为父节点即可。

三、代码实现

vector<int> father;
//名字->数量的映射
unordered_map<string, int> nameCnt;
//下标->名字的映射
unordered_map<int, string> indexName;
//名字->下标的映射
unordered_map<string, int> nameIndex;
void init(int n) {
	for (int i = 0; i < n; i++) {
		father.push_back(i);
	}
}
//查找(隔代压缩)
int findFather(int x) {
	while (x != father[x]) {
		father[x] = father[father[x]];
		x = father[x];
	}
	return x;
}
//合并
void Union(int a, int b) {
	int fa = findFather(a);
	int fb = findFather(b);
	if (fa != fb) {
		//字典序小的做父节点
		if (indexName[fa] < indexName[fb]) {
			father[fb] = fa;
			//更新频率
			nameCnt[indexName[fa]] += nameCnt[indexName[fb]];
		} else {
			father[fa] = fb;
			nameCnt[indexName[fb]] += nameCnt[indexName[fa]];
		}
	}
}
vector<string> trulyMostPopular(vector<string>& names, vector<string>& synonyms) {
	int n = names.size();
	if (n == 0) return {};
	//对数据做预处理
	for (int i = 0; i < n; i++) {
		int pos = names[i].find('(');
		string sub = names[i].substr(0, pos);
		string times = names[i].substr(pos + 1, names[i].size() - pos - 2);
		nameCnt[sub] = stoi(times);
		indexName[i] = sub;
		nameIndex[sub] = i;
	}
	vector<vector<string>> syName;
	for (int i = 0; i < synonyms.size(); i++) {
		int pos = synonyms[i].find(',');
		string sub1 = synonyms[i].substr(1, pos - 1);
		string sub2 = synonyms[i].substr(pos + 1, synonyms[i].size() -pos - 2 );
		syName.push_back({sub1, sub2});
	}
	init(n);
	for (auto& x : syName) {
		Union(nameIndex[x[0]], nameIndex[x[1]]);
	}
	vector<string> res;
	for (int i = 0; i < n; i++) {
		if (father[i] == i) {
			string ans = indexName[i] + "(" + to_string(nameCnt[indexName[i]]) + ")";
			res.push_back(ans);
		}
	}
	return res;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值