leetcode:婴儿名字(七)

161 阅读1分钟

题目:

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

题解:

class Solution {
    public String[] trulyMostPopular(String[] names, String[] synonyms) {
        Map<String, Integer> map = new HashMap<>();
        Map<String, String> unionMap = new HashMap<>();     //并查集, key(子孙)->value(祖宗)
        for (String name : names) {     //统计频率
            int idx1 = name.indexOf('(');
            int idx2 = name.indexOf(')');
            int frequency = Integer.valueOf(name.substring(idx1 + 1, idx2));//频率
            map.put(name.substring(0, idx1), frequency);//姓名
        }
        for (String pair : synonyms) {  //union同义词
            int idx = pair.indexOf(',');
            String name1 = pair.substring(1, idx);
            String name2 = pair.substring(idx + 1, pair.length() - 1);//找出本质相同的两个名字
            while (unionMap.containsKey(name1)) {   //找name1祖宗
                name1 = unionMap.get(name1);
            }
            while (unionMap.containsKey(name2)) {   //找name2祖宗
                name2 = unionMap.get(name2);
            }
            if(!name1.equals(name2)){   //祖宗不同,要合并
                int frequency = map.getOrDefault(name1, 0) + map.getOrDefault(name2, 0);    //出现次数是两者之和
                String trulyName = name1.compareTo(name2) < 0 ? name1 : name2;
                String nickName = name1.compareTo(name2) < 0 ? name2 : name1;
                unionMap.put(nickName, trulyName);      //小名作为大名的分支,即大名是小名的祖宗
                map.remove(nickName);       //更新一下数据
                map.put(trulyName, frequency);
            }
        }
        String[] res = new String[map.size()];
        int index = 0;
        for (String name : map.keySet()) {
            StringBuilder sb = new StringBuilder(name);
            sb.append('(');
            sb.append(map.get(name));
            sb.append(')');
            res[index++] = sb.toString();
        }
        return res;
    }
}

总结:

SubString():

    xx.substring()括号中带的参数不一样,效果就会有很大的区别,详细如下:

    xx.substring(0,2)表示取第一个和第二个字符(0,1,2表示第一、二、三个字符,含头不含尾的原则就只包含第一、二个字符),返回一个新的字符串(只包含指定的第一和第二个字符);

    xx.substring(2)表示去掉前两个字符,返回一个新的字符串(只包含去掉前两个字符后剩下的字符串)

Indexof():

  • public int indexOf(int ch): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

  • **public int indexOf(int ch, int fromIndex): ** 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

  • int indexOf(String str): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

  • int indexOf(String str, int fromIndex): 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

Map.getOrDefault():

    Map.getOrDefault(Object key, V defaultValue)方法的作用是:当Map集合中有这个key时,就使用这个key值;如果没有就使用默认值defaultValue。

containsKey ():

    boolean containsKey(Object key)如果此映射包含指定键的映射关系,则返回true。

compareto( ):

   compareTo() 方法用于两种方式的比较:字符串与对象进行比较 ; 按字典顺序比较两个字符串。返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。

  • 如果参数字符串等于此字符串,则返回值 0;
  • 如果此字符串小于字符串参数,则返回一个小于 0 的值;
  • 如果此字符串大于字符串参数,则返回一个大于 0 的值。toString()

ToString( ):

    将现有类型如Stingbuilder转换为一个string类型

标准的并查集构建流程,其中的思想是让本质相同的所有姓名都变成****key(子孙)->value(祖宗)

的形式,使得让字典序最小的那个成为最后的祖宗,在最后结果中展示出来。

还是那句话,看清楚并查集的思想到底是什么,并查集背后的数理逻辑到底是什么!