【LeetCode】每日一题 面试题 17.07. 婴儿名字

135 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

面试题 17.07. 婴儿名字

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

解题思路

题目不难,就是比较绕。
​
在扫描names数组之前要先知道哪些名字是相同的而且它们都应该应用哪一个字典序最小的名字。
​
把 synonyms 中的名字间关联关系建图,用BFS扫描相同的名字有哪些,然后按字典序排序得到字典序最小的名字。把所有的名字都指向这个字典序最小的名字。
​
然后对 names 数组做扫描,把次数统计到其相同名字中字典序最小的那个名字上,最后把map转换为数组,再输出结果字符串数组。

代码实现

var trulyMostPopular = function(names, synonyms) {
  const connectionMap = new Map()
  for(const synonym of synonyms) {
    const [name1, name2] = synonym.slice(1, synonym.length - 1).split(',')
    if(!connectionMap.has(name1)) connectionMap.set(name1, new Set())
    if(!connectionMap.has(name2)) connectionMap.set(name2, new Set())
    connectionMap.get(name1).add(name2)
    connectionMap.get(name2).add(name1)
  }
  const used = new Set()
  const sameNameMap = new Map()
  for(const name of connectionMap.keys()) {
    if(used.has(name)) continue
    used.add(name)
    let q = [name]
    const arr = []
    while(q.length) {
      const tq = []
      for(let i=0;i<q.length;i++) {
        const currName = q[i]
        arr.push(currName)
        const nexts = connectionMap.get(currName)
        for(let next of nexts) {
          if(!used.has(next)) {
            used.add(next)
            tq.push(next)
          }
        }
      }
      q = tq
    }
    arr.sort((a, b) => {
      if(a < b) return -1
      else if(a < b) return 1
      return 0
    })
    const target = arr[0]
    for(let e of arr) {
      sameNameMap.set(e, target)
    }
  }
  const resMap = new Map()
  for(let str of names) {
    let name = '', cnt = 0, i = 0
    while(str[i] !== '(') name += str[i++]
    cnt = Number(str.slice(i + 1, str.length - 1))
    const realName = getRealName(name)
    resMap.set(realName, (resMap.get(realName) || 0) + cnt)
  }
​
  function getRealName(s) {
    return sameNameMap.has(s) ? sameNameMap.get(s) : s
  }
  return Array.from(resMap).map(_ => {
    return `${_[0]}(${_[1]})`
  })
};

如果你对这道题目还有疑问的话,可以在评论区进行留言;