【并查集】面试题 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)”]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/baby-names-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


使用并查集求解,将names中的每个婴儿名字作为并查集的元素,通过遍历synonyms合并相同的婴儿名字,即确定元素间的父子关系,在合并时(即union函数),首先找到两个婴儿名字的祖宗,并将祖宗名字小的作为新的祖宗(题目要求选择字典序最小的名字作为真实名字)。

确定父子关系后,统计婴儿名字的频率,统计时先找到该名字的祖宗,然后将该名字的频率叠加到祖宗名字的频率上

class UnionFindSet {
	int parent[];

	public UnionFindSet(int n) {
		parent = new int[n];
		for (int i = 0; i < n; i++)
			parent[i] = -1;
	}

	public int find(int i) {
		if (parent[i] < 0)
			return i;
		parent[i] = find(parent[i]);
		return parent[i];
	}

	public void union(int i, int j, Map<Integer, String> nameIdx) {
		// 合并i和j, 并且让i j中祖宗的字典序最小的作为父亲, 并返回父亲坐标
		int ii = find(i);
		int jj = find(j);
		String nameii = nameIdx.get(ii);
		String namejj = nameIdx.get(jj);

		if (ii != jj) {
			if (nameii.compareTo(namejj) < 0)
				// nameii < namejj, let ii be the father.
				parent[jj] = ii;
			else
				parent[ii] = jj;
		}
	}
}

class Solution {
	public String[] trulyMostPopular(String[] names, String[] synonyms) {
		/**
		 * 输入:names = ["John(15)","Jon(12)","Chris(13)","Kris(4)","Christopher(19)"],
		 * synonyms = ["(Jon,John)","(John,Johnny)","(Chris,Kris)","(Kris,Christopher)"]
		 * 输出:["John(27)","Chris(36)"]
		 */
		int n = names.length;
		UnionFindSet ufset = new UnionFindSet(n);
		Map<String, Integer> nameIndexMap = new HashMap();
		Map<Integer, String> indexnameMap = new HashMap();
		for (int i = 0; i < n; i++) {
			// get name
			int idx1 = names[i].indexOf('(');
			int idx2 = names[i].indexOf(')');
			String name = names[i].substring(0, idx1);
			nameIndexMap.put(name, i);
			indexnameMap.put(i, name);
		}

		for (String pair : synonyms) {
			int idx1 = pair.indexOf('(');
			int idx2 = pair.indexOf(',');
			int idx3 = pair.indexOf(')');
			String name1 = pair.substring(idx1 + 1, idx2);
			String name2 = pair.substring(idx2 + 1, idx3);
			if (nameIndexMap.containsKey(name1) && nameIndexMap.containsKey(name2)) {
				int id1 = nameIndexMap.get(name1), id2 = nameIndexMap.get(name2);
				ufset.union(id1, id2, indexnameMap);
			}
		}
		Map<String, Integer> nameFreqMap = new HashMap<>();
		for (int i = 0; i < n; i++) {
			int f = ufset.find(i);
			// get frequency of name i.
			int freqi = Integer.valueOf(names[i].substring(names[i].indexOf('(') + 1, names[i].indexOf(')')));
			String namef = names[f].substring(0, names[f].indexOf('('));
			if (nameFreqMap.containsKey(namef)) {
				int count = nameFreqMap.get(namef) + freqi;
				nameFreqMap.remove(namef);
				nameFreqMap.put(namef, count);
			} else {
				nameFreqMap.put(namef, freqi);
			}
		}
		String res[] = new String[nameFreqMap.size()];
		int i = 0;
		for (String key : nameFreqMap.keySet()) {
			String tmp = key + '(' + nameFreqMap.get(key).toString() + ')';
			res[i] = tmp;
			i++;
		}

		return res;
	}
}

这里主要用到字符串的indexOfsubstring等操作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值