题号:no90
题目名:婴儿名字
原题URL:https://leetcode-cn.com/problems/baby-names-lcci/
题目描述
每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,则 John 与 Johnny 也相同,即它们有传递和对称性。
在结果列表中,选择字典序最小的名字作为真实名字。
示例
示例 1:
输入: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
思路
1.用散列+并查集来解决
2.先将所有名字及对应的每个名字的数量用散列存储起来
3.将所有应该在一起的集合进行存储,这里要注意,map.get得到的list并不是真正的指针,而是指针的副本,需要再重新去map里赋值
4.并查集+字典排序完成该题
解题代码
public class Solution {
public String[] trulyMostPopular(String[] names, String[] synonyms) {
//遍历名字数组保留名字-个数散列
Map<String,Integer> name2nums = new HashMap<>();
//保留名字并查集
Map<String,List<String>> name2List = new HashMap<> ();
//存下所有并查集
List<String> nameList = new ArrayList<>();
for (int i = 0; i < names.length; i++) {
int leftBucket = names[i].indexOf('(');
int rightBucket = names[i].indexOf(')');
name2nums.put(names[i].substring(0, leftBucket),Integer.parseInt(names[i].substring(leftBucket+1,rightBucket)));
}
for (String synonym : synonyms) {
//拿到同组的两个名字
String str1 = synonym.substring(synonym.indexOf('(')+1, synonym.indexOf(','));
String str2 = synonym.substring(synonym.indexOf(',')+1, synonym.indexOf(')'));
List<String> list1 = name2List.get(str1);
List<String> list2 = name2List.get(str2);
//如果这两个名字都没有集合,那么就新建一个集合加入
if(list1==null&&list2==null) {
List<String> list = new ArrayList<>();
list.add(str1);
list.add(str2);
name2List.put(str1,list);
name2List.put(str2,list);
}else {
//如果第一个为空
if(list1==null) {
list2.add(str1);
name2List.put(str1,list2);
} else if(list2==null) {
//如果第二个为空
list1.add(str2);
name2List.put(str2,list1);
} else {
//如果两个都不为空
List<String> newList = new ArrayList<> (list1);
for (String s : list2) {
if(!newList.contains(s)) newList.add(s);
}
for (String s : newList) {
name2List.put(s,newList);
}
}
}
}
for (Map.Entry<String, Integer> entry : name2nums.entrySet()) {
//如果没有值,说明值被清空了
Integer value = entry.getValue();
if(value ==null) continue;
//如果没有并查集,直接将这个数插入
StringBuilder sb = new StringBuilder();
String key = entry.getKey();
List<String> conbineList = name2List.get(key);
if(conbineList ==null) {
sb.append(key).append('(').append(value).append(')');
nameList.add(sb.toString());
}else {
//如果有并查集,字典排序
conbineList.sort((s1,s2)->s1.compareTo(s2));
int sum = 0;
sb.append(conbineList.get(0)).append('(');
for (String s : conbineList) {
Integer integer = name2nums.get(s);
if(integer!=null) {
sum+=integer;
//加和后将这个key置位null,下次就不会用这个键了
name2nums.put(s,null);
}
}
sb.append(sum).append(')');
nameList.add(sb.toString());
}
}
return nameList.toArray(new String[0]);
}