Open In App

Email Account Merging

Last Updated : 31 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given a list of accounts where each element accounts[i] is a list of strings, where the first element account[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 common email 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.

Note: Accounts themselves can be returned in any order.

Example:

Input: N = 4, accounts [ ] = [[“John”, “johnsmith@mail.com”, “john_newyork@mail.com”], [“John”, “johnsmith@mail.com”, “john00@mail.com”], [“Mary”, “mary@mail.com”], [“John”, “johnnybravo@mail.com”]]
Output: [[“John”,”john00@mail.com”,”john_newyork@mail.com”, “johnsmith@mail.com”],[“Mary”,”mary@mail.com”], [“John”,”johnnybravo@mail.com”]]
Explanation:
The first and second John’s are the same person as they have the common email “johnsmith@mail.com”. The third John and Mary are different people as none of their email addresses are used by other accounts. We could return these arrays 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.

Input: N = 5, accounts [ ] = [[“Gabe”,”Gabe00@m.co”,”Gabe3@m.co”,”Gabe1@m.co”],[“Kevin”,”Kevin3@m.co”,”Kevin5@m.co”,”Kevin0@m.co”],[“Ethan”,”Ethan5@m.co”,”Ethan4@m.co”,”Ethan0@m.co”],[“Hanzo”,”Hanzo3@m.co”,”Hanzo1@m.co”,”Hanzo0@m.co”],[“Fern”,”Fern5@m.co”,”Fern1@m.co”,”Fern0@m.co”]]
Output:
[[“Ethan”,”Ethan0@m.co”,”Ethan4@m.co”,”Ethan5@m.co”],[“Gabe”,”Gabe0@m.co”,”Gabe1@m.co”,”Gabe3@m.co”],[“Hanzo”,”Hanzo0@m.co”,”Hanzo1@m.co”,”Hanzo3@m.co”],[“Kevin”,”Kevin0@m.co”,”Kevin3@m.co”,”Kevin5@m.co”],[“Fern”,”Fern0@m.co”,”Fern1@m.co”,”Fern5@m.co”]]
Explanation:
We don’t have any common emails in any of the users. We just sorted the emails of each person and we return a array of emails .(The details can be returned in any order).

Approach:

The implementation uses Disjoint Set Union (DSU) to group emails that belong to the same account. It iterates through the given accounts, and for each email encountered, it either adds it to an existing set (if the email is already encountered) or creates a new set. After grouping emails, it constructs the final result by sorting the emails within each set and combining them with the account name. This ensures that emails belonging to the same account are grouped together, fulfilling the requirements of the problem.

Steps-by-step approach:

  • Initialize a Disjoint Set Union (DSU) data structure to keep track of connected emails.
  • Iterate through the given accounts, and for each email encountered:
    • If the email is already in the DSU, union the sets to which the current account and email belong.
    • Otherwise, add the email to the DSU and associate it with the current account.
  • After processing all accounts, create groups of emails using DSU’s find operation.
  • Sort the emails within each group and combine them with the corresponding account name.
  • Collect the final result and return the merged accounts.

Below is the implementation of the above approach:

C++




#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <vector>
 
using namespace std;
 
// Class for implementing Disjoint Set Union (DSU) data
// structure
class DisjointSet {
    vector<int> parent, size;
 
public:
    DisjointSet(int n)
    {
        parent.resize(n);
        size.resize(n, 1);
 
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }
 
    int findPar(int node)
    {
        // Find the root of the set to which 'node' belongs
        if (node == parent[node]) {
            return node;
        }
        // Path compression: Make the parent of 'node' the
        // root
        return parent[node] = findPar(parent[node]);
    }
 
    void unionBySize(int u, int v)
    {
        // Union by size to merge two sets
        int par_u = findPar(u);
        int par_v = findPar(v);
 
        if (par_u == par_v) {
            return;
        }
        if (size[par_u] < size[par_v]) {
            parent[par_u] = par_v;
            size[par_v] += size[par_u];
        }
        else {
            parent[par_v] = par_u;
            size[par_u] += size[par_v];
        }
    }
};
 
class Solution {
public:
    vector<vector<string> >
    accountsMerge(vector<vector<string> >& accounts)
    {
        unordered_map<string, int> mails;
        int n = accounts.size();
        DisjointSet ds(n);
 
        // Iterate through accounts and build disjoint sets
        for (int i = 0; i < n; i++) {
            for (int j = 1; j < accounts[i].size(); j++) {
                string mail = accounts[i][j];
                if (mails.find(mail) == mails.end()) {
                    mails[mail] = i;
                }
                else {
                    ds.unionBySize(i, mails[mail]);
                }
            }
        }
 
        // Group merged emails based on disjoint sets
        vector<string> mergedMails[n];
        for (auto it : mails) {
            int node = ds.findPar(it.second);
            string mail = it.first;
            mergedMails[node].push_back(mail);
        }
 
        // Construct the final result
        vector<vector<string> > ans;
        for (int i = 0; i < n; i++) {
            if (mergedMails[i].size() == 0) {
                continue;
            }
            vector<string> temp;
            temp.push_back(accounts[i][0]);
            sort(mergedMails[i].begin(),
                 mergedMails[i].end());
            for (auto it : mergedMails[i]) {
                temp.push_back(it);
            }
            ans.push_back(temp);
        }
        return ans;
    }
};
 
int main()
{
    // Input
    int N = 4;
    vector<vector<string> > accounts
        = { { "John", "johnsmith@mail.com",
              "john_newyork@mail.com" },
            { "John", "johnsmith@mail.com",
              "john00@mail.com" },
            { "Mary", "mary@mail.com" },
            { "John", "johnnybravo@mail.com" } };
 
    // Create an instance of the Solution class
    Solution obj;
 
    // Call the accountsMerge function
    vector<vector<string> > result
        = obj.accountsMerge(accounts);
 
    // Display the result
    for (auto& vec : result) {
        for (auto& str : vec) {
            cout << str << " ";
        }
        cout << endl;
    }
 
    return 0;
}


Java




import java.util.*;
 
// Class for implementing Disjoint Set Union (DSU) data structure
class DisjointSet {
    private int[] parent, size;
 
    public DisjointSet(int n) {
        parent = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }
 
    public int findPar(int node) {
        // Find the root of the set to which 'node' belongs
        if (node == parent[node]) {
            return node;
        }
        // Path compression: Make the parent of 'node' the root
        return parent[node] = findPar(parent[node]);
    }
 
    public void unionBySize(int u, int v) {
        // Union by size to merge two sets
        int par_u = findPar(u);
        int par_v = findPar(v);
 
        if (par_u == par_v) {
            return;
        }
        if (size[par_u] < size[par_v]) {
            parent[par_u] = par_v;
            size[par_v] += size[par_u];
        } else {
            parent[par_v] = par_u;
            size[par_u] += size[par_v];
        }
    }
}
 
class Solution {
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        Map<String, Integer> mails = new HashMap<>();
        int n = accounts.size();
        DisjointSet ds = new DisjointSet(n);
 
        // Iterate through accounts and build disjoint sets
        for (int i = 0; i < n; i++) {
            for (int j = 1; j < accounts.get(i).size(); j++) {
                String mail = accounts.get(i).get(j);
                if (!mails.containsKey(mail)) {
                    mails.put(mail, i);
                } else {
                    ds.unionBySize(i, mails.get(mail));
                }
            }
        }
 
        // Group merged emails based on disjoint sets
        List<String>[] mergedMails = new ArrayList[n];
        for (Map.Entry<String, Integer> entry : mails.entrySet()) {
            int node = ds.findPar(entry.getValue());
            String mail = entry.getKey();
            if (mergedMails[node] == null) {
                mergedMails[node] = new ArrayList<>();
            }
            mergedMails[node].add(mail);
        }
 
        // Construct the final result
        List<List<String>> ans = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            if (mergedMails[i] == null || mergedMails[i].size() == 0) {
                continue;
            }
            List<String> temp = new ArrayList<>();
            temp.add(accounts.get(i).get(0));
            Collections.sort(mergedMails[i]);
            temp.addAll(mergedMails[i]);
            ans.add(temp);
        }
        return ans;
    }
}
 
public class Main {
    public static void main(String[] args) {
        // Input
        int N = 4;
        List<List<String>> accounts = Arrays.asList(
                Arrays.asList("John", "johnsmith@mail.com", "john_newyork@mail.com"),
                Arrays.asList("John", "johnsmith@mail.com", "john00@mail.com"),
                Arrays.asList("Mary", "mary@mail.com"),
                Arrays.asList("John", "johnnybravo@mail.com")
        );
 
        // Create an instance of the Solution class
        Solution obj = new Solution();
 
        // Call the accountsMerge function
        List<List<String>> result = obj.accountsMerge(accounts);
 
        // Display the result
        for (List<String> vec : result) {
            for (String str : vec) {
                System.out.print(str + " ");
            }
            System.out.println();
        }
    }
}


Python3




from typing import List
from collections import defaultdict
 
 
# Class for implementing Disjoint Set Union (DSU) data structure
class DisjointSet:
    def __init__(self, n):
        self.parent = [i for i in range(n)]
        self.size = [1] * n
 
    def find_par(self, node):
        # Find the root of the set to which 'node' belongs
        if node == self.parent[node]:
            return node
        # Path compression: Make the parent of 'node' the root
        self.parent[node] = self.find_par(self.parent[node])
        return self.parent[node]
 
    def union_by_size(self, u, v):
        # Union by size to merge two sets
        par_u = self.find_par(u)
        par_v = self.find_par(v)
 
        if par_u == par_v:
            return
        if self.size[par_u] < self.size[par_v]:
            self.parent[par_u] = par_v
            self.size[par_v] += self.size[par_u]
        else:
            self.parent[par_v] = par_u
            self.size[par_u] += self.size[par_v]
 
 
class Solution:
    def accounts_merge(self, accounts: List[List[str]]) -> List[List[str]]:
        mails = {}
        n = len(accounts)
        ds = DisjointSet(n)
 
        # Iterate through accounts and build disjoint sets
        for i in range(n):
            for j in range(1, len(accounts[i])):
                mail = accounts[i][j]
                if mail not in mails:
                    mails[mail] = i
                else:
                    ds.union_by_size(i, mails[mail])
 
        # Group merged emails based on disjoint sets
        merged_mails = defaultdict(list)
        for mail, idx in mails.items():
            node = ds.find_par(idx)
            merged_mails[node].append(mail)
 
        # Construct the final result
        ans = []
        for i in range(n):
            if i not in merged_mails or not merged_mails[i]:
                continue
            temp = [accounts[i][0]]
            temp.extend(sorted(merged_mails[i]))
            ans.append(temp)
 
        return ans
 
 
# Driver Code
if __name__ == "__main__":
    # Input
    accounts = [
        ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
        ["John", "johnsmith@mail.com", "john00@mail.com"],
        ["Mary", "mary@mail.com"],
        ["John", "johnnybravo@mail.com"]
    ]
 
    # Create an instance of the Solution class
    obj = Solution()
 
    # Call the accounts_merge function
    result = obj.accounts_merge(accounts)
 
    # Display the result
    for vec in result:
        print(" ".join(vec))


C#




// C# Implementation
 
 
using System;
using System.Collections.Generic;
using System.Linq;
 
public class DisjointSet
{
    private int[] parent;
    private int[] size;
 
    public DisjointSet(int n)
    {
        parent = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++)
        {
            parent[i] = i;
            size[i] = 1;
        }
    }
 
    public int FindPar(int node)
    {
        if (node == parent[node])
        {
            return node;
        }
        return parent[node] = FindPar(parent[node]);
    }
 
    public void UnionBySize(int u, int v)
    {
        int par_u = FindPar(u);
        int par_v = FindPar(v);
 
        if (par_u == par_v)
        {
            return;
        }
        if (size[par_u] < size[par_v])
        {
            parent[par_u] = par_v;
            size[par_v] += size[par_u];
        }
        else
        {
            parent[par_v] = par_u;
            size[par_u] += size[par_v];
        }
    }
}
 
public class Solution
{
    public List<List<string>> AccountsMerge(List<List<string>> accounts)
    {
        Dictionary<string, int> mails = new Dictionary<string, int>();
        int n = accounts.Count;
        DisjointSet ds = new DisjointSet(n);
 
        for (int i = 0; i < n; i++)
        {
            for (int j = 1; j < accounts[i].Count; j++)
            {
                string mail = accounts[i][j];
                if (!mails.ContainsKey(mail))
                {
                    mails.Add(mail, i);
                }
                else
                {
                    ds.UnionBySize(i, mails[mail]);
                }
            }
        }
 
        List<string>[] mergedMails = new List<string>[n];
        foreach (KeyValuePair<string, int> entry in mails)
        {
            int node = ds.FindPar(entry.Value);
            string mail = entry.Key;
            if (mergedMails[node] == null)
            {
                mergedMails[node] = new List<string>();
            }
            mergedMails[node].Add(mail);
        }
 
        List<List<string>> ans = new List<List<string>>();
        for (int i = 0; i < n; i++)
        {
            if (mergedMails[i] == null || mergedMails[i].Count == 0)
            {
                continue;
            }
            List<string> temp = new List<string>();
            temp.Add(accounts[i][0]);
            mergedMails[i].Sort();
            temp.AddRange(mergedMails[i]);
            ans.Add(temp);
        }
        return ans;
    }
}
 
public class Program
{
    public static void Main(string[] args)
    {
         
        List<List<string>> accounts = new List<List<string>>()
        {
            new List<string>() { "John", "johnsmith@mail.com", "john_newyork@mail.com" },
            new List<string>() { "John", "johnsmith@mail.com", "john00@mail.com" },
            new List<string>() { "Mary", "mary@mail.com" },
            new List<string>() { "John", "johnnybravo@mail.com" }
        };
 
        Solution obj = new Solution();
        List<List<string>> result = obj.AccountsMerge(accounts);
 
        foreach (List<string> vec in result)
        {
            foreach (string str in vec)
            {
                Console.Write(str + " ");
            }
            Console.WriteLine();
        }
    }
}
 
// This code is contributed by Tapesh(tapeshdu420)


Javascript




// Javascript code for the above approach
 
class DisjointSet {
    // Class for implementing Disjoint Set
    // Union (DSU) data structure
    constructor(n) {
        // Initialize parent and size arrays
        this.parent = Array(n);
        this.size = Array(n).fill(1);
 
        // Initialize each element as a
        // disjoint set with size 1
        for (let i = 0; i < n; i++) {
            this.parent[i] = i;
        }
    }
 
    // Find the root of the set
    // to which 'node' belongs
    findPar(node) {
        if (node === this.parent[node]) {
            return node;
        }
        // Path compression:
        // Make the parent of 'node' the root
        return (this.parent[node] =
                            this.findPar(this.parent[node]));
    }
 
    // Union by size to merge two sets
    unionBySize(u, v) {
        const parU = this.findPar(u);
        const parV = this.findPar(v);
 
        if (parU === parV) {
            return;
        }
 
        // Attach the smaller set to the larger set
        if (this.size[parU] < this.size[parV]) {
            this.parent[parU] = parV;
            this.size[parV] += this.size[parU];
        } else {
            this.parent[parV] = parU;
            this.size[parU] += this.size[parV];
        }
    }
}
 
class Solution {
    // Class for the solution
    accountsMerge(accounts) {
        // Hash map to store mail and
        // corresponding account index
        const mails = new Map();
        const n = accounts.length;
        // Create an instance of the
        // Disjoint Set class
        const ds = new DisjointSet(n);
 
        // Iterate through accounts and build disjoint sets
        for (let i = 0; i < n; i++) {
            for (let j = 1; j < accounts[i].length; j++) {
                const mail = accounts[i][j];
                // If mail is not in the map, assign it
                // to the current account index
                if (!mails.has(mail)) {
                    mails.set(mail, i);
                } else {
                    // If mail is in the map, merge the
                    // corresponding sets
                    ds.unionBySize(i, mails.get(mail));
                }
            }
        }
 
        // Group merged emails based on disjoint sets
        const mergedMails = Array.from({
            length: n
        }, () => []);
 
        for (const [mail, index] of mails) {
            const node = ds.findPar(index);
            mergedMails[node].push(mail);
        }
 
        // Construct the final result
        const ans = [];
 
        for (let i = 0; i < n; i++) {
            if (mergedMails[i].length === 0) {
                continue;
            }
            const temp = [accounts[i][0]];
            temp.push(...mergedMails[i].sort());
            ans.push(temp);
        }
 
        return ans;
    }
}
 
// Driver code
function main() {
    // Input
    const N = 4;
    const accounts = [
        ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
        ["John", "johnsmith@mail.com", "john00@mail.com"],
        ["Mary", "mary@mail.com"],
        ["John", "johnnybravo@mail.com"]
    ];
 
    // Create an instance of the Solution class
    const obj = new Solution();
 
    // Call the accountsMerge function
    const result = obj.accountsMerge(accounts);
 
    // Display the result
    for (const vec of result) {
        console.log(vec.join(" "));
    }
}
 
// Driver call
main();
 
// This code is contributed by ragul21


Output

John john00@mail.com john_newyork@mail.com johnsmith@mail.com 
Mary mary@mail.com 
John johnnybravo@mail.com 

Time Complexity: O(N * (M + α(N))), where N is the number of accounts and M is the average number of emails in each account. α(N) is the amortized time complexity of the Disjoint Set Union (DSU) operations.
Auxiliary Space: O(N * M), where N is the number of accounts and M is the average number of emails in each account. The space is primarily used for storing the DSU data structure and the merged emails.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads