跳转至

面试题 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)"]

提示:

  • names.length <= 100000

解法

方法一:哈希表 + DFS

对于每个同义词对,我们将其两个名字建立双向边,存放在邻接表 $g$ 中,然后,我们遍历所有名字,将其存放在集合 $s$ 中,同时将其频率存放在哈希表 $cnt$ 中。

接下来,我们遍历集合 $s$ 中的每个名字,如果该名字未被访问过,则进行深度优先搜索,找到该名字所在的连通分量中的所有名字,以字典序最小的名字为真实名字,将其频率求和,即为真实名字的频率。然后,我们将该名字以及其频率存放在答案数组中。

遍历完所有名字后,答案数组即为所求。

时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别为名字数组和同义词数组的长度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution:
    def trulyMostPopular(self, names: List[str], synonyms: List[str]) -> List[str]:
        def dfs(a):
            vis.add(a)
            mi, x = a, cnt[a]
            for b in g[a]:
                if b not in vis:
                    t, y = dfs(b)
                    if mi > t:
                        mi = t
                    x += y
            return mi, x

        g = defaultdict(list)
        for e in synonyms:
            a, b = e[1:-1].split(',')
            g[a].append(b)
            g[b].append(a)
        s = set()
        cnt = defaultdict(int)
        for x in names:
            name, freq = x[:-1].split("(")
            s.add(name)
            cnt[name] = int(freq)
        vis = set()
        ans = []
        for name in s:
            if name not in vis:
                name, freq = dfs(name)
                ans.append(f"{name}({freq})")
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution {
    private Map<String, List<String>> g = new HashMap<>();
    private Map<String, Integer> cnt = new HashMap<>();
    private Set<String> vis = new HashSet<>();
    private int freq;

    public String[] trulyMostPopular(String[] names, String[] synonyms) {
        for (String pairs : synonyms) {
            String[] pair = pairs.substring(1, pairs.length() - 1).split(",");
            String a = pair[0], b = pair[1];
            g.computeIfAbsent(a, k -> new ArrayList<>()).add(b);
            g.computeIfAbsent(b, k -> new ArrayList<>()).add(a);
        }
        Set<String> s = new HashSet<>();
        for (String x : names) {
            int i = x.indexOf('(');
            String name = x.substring(0, i);
            s.add(name);
            cnt.put(name, Integer.parseInt(x.substring(i + 1, x.length() - 1)));
        }
        List<String> res = new ArrayList<>();
        for (String name : s) {
            if (!vis.contains(name)) {
                freq = 0;
                name = dfs(name);
                res.add(name + "(" + freq + ")");
            }
        }
        return res.toArray(new String[0]);
    }

    private String dfs(String a) {
        String mi = a;
        vis.add(a);
        freq += cnt.getOrDefault(a, 0);
        for (String b : g.getOrDefault(a, new ArrayList<>())) {
            if (!vis.contains(b)) {
                String t = dfs(b);
                if (t.compareTo(mi) < 0) {
                    mi = t;
                }
            }
        }
        return mi;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
public:
    vector<string> trulyMostPopular(vector<string>& names, vector<string>& synonyms) {
        unordered_map<string, vector<string>> g;
        unordered_map<string, int> cnt;
        for (auto& e : synonyms) {
            int i = e.find(',');
            string a = e.substr(1, i - 1);
            string b = e.substr(i + 1, e.size() - i - 2);
            g[a].emplace_back(b);
            g[b].emplace_back(a);
        }
        unordered_set<string> s;
        for (auto& e : names) {
            int i = e.find('(');
            string name = e.substr(0, i);
            s.insert(name);
            cnt[name] += stoi(e.substr(i + 1, e.size() - i - 2));
        }
        unordered_set<string> vis;
        int freq = 0;

        function<string(string)> dfs = [&](string a) -> string {
            string res = a;
            vis.insert(a);
            freq += cnt[a];
            for (auto& b : g[a]) {
                if (!vis.count(b)) {
                    string t = dfs(b);
                    if (t < res) {
                        res = move(t);
                    }
                }
            }
            return move(res);
        };

        vector<string> ans;
        for (auto& name : s) {
            if (!vis.count(name)) {
                freq = 0;
                string x = dfs(name);
                ans.emplace_back(x + "(" + to_string(freq) + ")");
            }
        }
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func trulyMostPopular(names []string, synonyms []string) (ans []string) {
    g := map[string][]string{}
    for _, s := range synonyms {
        i := strings.Index(s, ",")
        a, b := s[1:i], s[i+1:len(s)-1]
        g[a] = append(g[a], b)
        g[b] = append(g[b], a)
    }
    s := map[string]struct{}{}
    cnt := map[string]int{}
    for _, e := range names {
        i := strings.Index(e, "(")
        name, num := e[:i], e[i+1:len(e)-1]
        x, _ := strconv.Atoi(num)
        cnt[name] += x
        s[name] = struct{}{}
    }
    freq := 0
    vis := map[string]struct{}{}
    var dfs func(string) string
    dfs = func(a string) string {
        vis[a] = struct{}{}
        freq += cnt[a]
        res := a
        for _, b := range g[a] {
            if _, ok := vis[b]; !ok {
                t := dfs(b)
                if t < res {
                    res = t
                }
            }
        }
        return res
    }
    for name := range s {
        if _, ok := vis[name]; !ok {
            freq = 0
            root := dfs(name)
            ans = append(ans, root+"("+strconv.Itoa(freq)+")")
        }
    }
    return
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function trulyMostPopular(names: string[], synonyms: string[]): string[] {
    const map = new Map<string, string>();
    for (const synonym of synonyms) {
        const [k1, k2] = [...synonym]
            .slice(1, synonym.length - 1)
            .join('')
            .split(',');
        const [v1, v2] = [map.get(k1) ?? k1, map.get(k2) ?? k2];
        const min = v1 < v2 ? v1 : v2;
        const max = v1 < v2 ? v2 : v1;
        map.set(k1, min);
        map.set(k2, min);
        for (const [k, v] of map.entries()) {
            if (v === max) {
                map.set(k, min);
            }
        }
    }

    const keyCount = new Map<string, number>();
    for (const name of names) {
        const num = name.match(/\d+/)[0];
        const k = name.split('(')[0];
        const key = map.get(k) ?? k;
        keyCount.set(key, (keyCount.get(key) ?? 0) + Number(num));
    }
    return [...keyCount.entries()].map(([k, v]) => `${k}(${v})`);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Solution {
    private var graph = [String: [String]]()
    private var count = [String: Int]()
    private var visited = Set<String>()
    private var freq: Int = 0

    func trulyMostPopular(_ names: [String], _ synonyms: [String]) -> [String] {
        for pair in synonyms {
            let cleanPair = pair.dropFirst().dropLast()
            let parts = cleanPair.split(separator: ",").map(String.init)
            let a = parts[0], b = parts[1]
            graph[a, default: []].append(b)
            graph[b, default: []].append(a)
        }

        var namesSet = Set<String>()
        for name in names {
            let index = name.firstIndex(of: "(")!
            let realName = String(name[..<index])
            namesSet.insert(realName)
            let num = Int(name[name.index(after: index)..<name.index(before: name.endIndex)])!
            count[realName] = num
        }

        var result = [String]()
        for name in namesSet {
            if !visited.contains(name) {
                freq = 0
                let representative = dfs(name)
                result.append("\(representative)(\(freq))")
            }
        }

        return result
    }

    private func dfs(_ name: String) -> String {
        var minName = name
        visited.insert(name)
        freq += count[name, default: 0]
        for neighbor in graph[name, default: []] {
            if !visited.contains(neighbor) {
                let temp = dfs(neighbor)
                if temp < minName {
                    minName = temp
                }
            }
        }
        return minName
    }
}

评论