721. Accounts Merge


Given a list accounts, each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.

Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some email that is common to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.

After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.

Example 1:

Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
The first and third John's are the same person as they have the common email "johnsmith@mail.com".
The second John and Mary are different people as none of their email addresses are used by other accounts.
We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], 
['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted.

Note:

  1. The length of accounts will be in the range [1, 1000].
  2. The length of accounts[i] will be in the range [1, 10].
  3. The length of accounts[i][j] will be in the range [1, 30].

Hint: For every pair of emails in the same account, draw an edge between those emails. The problem is about enumerating the connected components of this graph.

方法1:union find

grandyang: http://www.cnblogs.com/grandyang/p/7829169.html

思路:

一道非常好的union find题目。本质上就是hint中所讲的,找到以每个人为vertex的这幅图中所有connected component。由于每个vertex不是数字,需要用hashmap来代替一般的vector<\int> root。在初始化的时候,每个email的root都是自己。其次,我们在图上画两种edge:同一个vector之间的,以及,不同vector之间的同样的email。而画edge在union find当中就相当于union这些vertex。在完成这些操作之后,建立一个unordered_map<string, set<\string>> res; x形式的temporary result,储存着{parent: set<\string>}, i.e. 每个root中的parent节点所连接的所有节点。最后通过owner这个{email: name}的反映射找回connected component对应的名字,推进最终的vector<vector<\int>> result。

易错点

  1. root[parent1] = parent2:这个union的形式并没有改变,很容易错
  2. 为什么这个res的map不能是{user: set<\string> emails}一步到位:这是这道题的核心point。。不能因为名字一样就merge到一起,能够起key作用的是root中的parent。
  3. 最后要求每个人的email有序:要用set<\string> 来做pre_res,同时起到去重的作用。

Complexity

Time complexity: O(m * n)
Space complexity: O(m * n)

class Solution {
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        vector<vector<string>> result;
        unordered_map<string, string> root;
        unordered_map<string, string> owner;
        unordered_map<string, set<string>> res;
        
        for (auto account: accounts) {
            for (int i = 1; i < account.size(); i++) {
                root[account[i]] = account[i];
                owner[account[i]] = account[0];
            }
        }
        
        for (auto account: accounts) {
            string parent = unionHelper(root, account[1]);
            for (int i = 2; i < account.size(); i++) {
                root[unionHelper(root, account[i])] = parent;
            }
        }
        
        
        for (auto account: accounts) {
            for (int i = 1; i < account.size(); i++) {
                res[unionHelper(root, account[i])].insert(account[i]);
            }
        }
        
        
        for (auto item: res) {
            vector<string> tmp(item.second.begin(), item.second.end());
            tmp.insert(tmp.begin(), owner[item.first]); 
            result.push_back(tmp);
        }
        return result;
    }
    
    string unionHelper(unordered_map<string, string> & root, string em) {
        while (em != root[em]) {
            root[em] = root[root[em]];
            em = root[em];
        }
        return em;
    }
};

class Solution {
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        vector<vector<string>> res;
        unordered_map<string, string> owner;
        unordered_map<string, string> root;
        unordered_map<string, set<string>> pre_res; // {account[1]: {emails}}
        for (auto account: accounts) {
            for (int i = 1; i < account.size(); i++) {
                root[account[i]] = account[i];
                owner[account[i]] = account[0];
            }
        }
        
        for (auto account: accounts) {
            string par = find_op(root, account[1]);
            for (int i = 2; i < account.size(); i++) {
                // if (find_op(root, account[i]) != par) {
                    root[find_op(root, account[i])] = par;
                // }
            }
        }
        
        for (auto account: accounts) {
            for (int i = 1; i < account.size(); i++) {
                pre_res[find_op(root, account[i])].insert(account[i]);
            }
        }
        
        for (auto item: pre_res) {
            vector<string> tmp = {owner[item.first]};
            tmp.insert(tmp.end(), item.second.begin(), item.second.end());
            res.push_back(tmp);
        }
        return res;
    }
    
    string find_op(unordered_map<string, string> & root, string i) {
        while (root[i] != i) {
            i = root[i];
        }
        return i;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值