机试题——通讯录归并

打印 上一主题 下一主题

主题 879|帖子 879|积分 2637

题目描述

你有一个通讯录,每个联系人包含姓名和手机号,一个联系人大概有多个手机号。假如两个联系人拥有相同的手机号,我们以为他们是同一个人。任务是整理通讯录,将具有相同手机号的联系人归并为一个联系人,并返回归并后的通讯录列表。假如归并时发现联系人姓名差别,以字典序小的为姓名。
输入描述


  • 第一行:一个整数num,表示通讯录的记录数量(1 <= num <= 1000)。
  • 接下来num行:每行表示一条联系人记录,包含姓名和多少电话号码(电话号码个数不超过10,长度在1到10之间)。姓名由英文字母大小写构成,长度在1到10之间。
输出描述

整理后的通讯录列表,此中具有相同手机号的联系人归并为一个联系人。输出联系人姓名和手机号码列表按ASCII码升序排序。
用例输入

  1. 4
  2. kaka 10000000000 10000000001
  3. tata 10000000020
  4. kaka 10000000000 10000000002
  5. tata 10000000010
复制代码
  1. kaka 10000000000 10000000001 10000000002
  2. tata 10000000010
  3. tata 10000000020
复制代码
第一个kaka和第二个kaka有相同的手机号10000000000,因此他们被视为同一个人。归并后的手机号按ASCII码升序排序。
  1. 3
  2. kaka 10000000000 10000000001
  3. kaka 10000000001 10000000002
  4. kaka 10000000002 10000000003
复制代码
  1. kaka 10000000000 10000000001 10000000002 10000000003
复制代码
第一个kaka和第二个kaka有相同的手机号10000000001,第二个kaka和第三个kaka有相同的手机号10000000002,因此他们被视为同一个人。归并后的手机号按ASCII码升序排序。
解题思路


  • 题目建模

    • 使用并查集来归并具有相同手机号的联系人。
    • 使用哈希表记录每个手机号对应的联系人索引。
    • 使用集合去重并排序手机号。

  • 并查集

    • 对于每个联系人的手机号,假如该手机号已经出现过,则将当前联系人与之前出现的联系人归并。
    • 使用路径压缩优化并查集。

  • 归并联系人

    • 对于每个联系人,找到其根节点(即归并后的代表)。
    • 归并手机号,去重并排序。
    • 假如姓名差别,选择字典序小的姓名。

  • 输出结果

    • 按姓名和手机号的ASCII码升序排序,输出归并后的通讯录。

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<map>
  6. #include<algorithm>
  7. #include<string>
  8. #include<vector>
  9. #include<unordered_map>
  10. #include<unordered_set>
  11. #include<queue>
  12. #include<set>
  13. #include<list>
  14. #include<sstream>
  15. #include<bitset>
  16. #include<stack>
  17. #include<climits>
  18. #include<iomanip>
  19. #include<cstdint>
  20. using namespace std;
  21. // 并查集
  22. vector<int> f(10005);
  23. int find(int x) {
  24.     if (f[x] == x) return x;
  25.     else return f[x] = find(f[x]);
  26. }
  27. void un(int x, int y) {
  28.     int fx = find(x);
  29.     f[fx] = y;
  30. }
  31. int main() {
  32.     ios::sync_with_stdio(false);  // 关闭与 C 的同步
  33.     cin.tie(nullptr);             // 解除 cin 与 cout 的绑定
  34.     int n;
  35.     cin >> n;
  36.     cin.ignore();
  37.     vector<string> name(n);
  38.     vector<vector<string>> phone(n);
  39.     map<string, int> memo; // 记录手机号对应的联系人索引
  40.     for (int i = 0; i < n; i++) {
  41.         f[i] = i; // 初始化并查集
  42.         string input, temp;
  43.         getline(cin, input);
  44.         istringstream is(input);
  45.         is >> name[i];
  46.         while (is >> temp) {
  47.             if (memo.count(temp)) { // 如果手机号已经出现过
  48.                 un(memo[temp], i); // 合并两个联系人
  49.             }
  50.             memo[temp] = i; // 更新手机号对应的联系人索引
  51.             phone[i].push_back(temp); // 记录当前联系人的手机号
  52.         }
  53.     }
  54.     // 开始合并
  55.     map<int, vector<int>> unio; // 父节点和其子集
  56.     map<string, vector<vector<string>>> res; // 一个名字可能对应多个通讯录号
  57.     for (int i = 0; i < n; i++) {
  58.         int cf = find(i); // 找到当前联系人的根节点
  59.         unio[cf].push_back(i); // 合并到同一个集合
  60.     }
  61.     // 排序并输出结果
  62.     for (auto& f : unio) {
  63.         string cur_name;
  64.         set<string> cur_phone; // 去重并排序手机号
  65.         for (int c : f.second) {
  66.             // 查询当前集合的名字最小值
  67.             if (cur_name.size() == 0)
  68.                 cur_name = name[c];
  69.             else
  70.                 cur_name = min(cur_name, name[c]);
  71.             // 合并当前联系人的所有手机号
  72.             for (int i = 0; i < phone[c].size(); i++) {
  73.                 cur_phone.insert(phone[c][i]);
  74.             }
  75.         }
  76.         vector<string> v_phone(cur_phone.begin(), cur_phone.end());
  77.         res[cur_name].push_back(v_phone); // 保存结果
  78.     }
  79.     // 按姓名和手机号的ASCII码升序排序
  80.     for (auto& t : res) {
  81.         sort(t.second.begin(), t.second.end());
  82.         for (auto c : t.second) {
  83.             cout << t.first;
  84.             for (int i = 0; i < c.size(); i++)
  85.                 cout << " " << c[i];
  86.             cout << "\n";
  87.         }
  88.     }
  89.     return 0;
  90. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

九天猎人

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表