目录
一、题目描述
每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,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;
}