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:
#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;
} |
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();
}
}
} |
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# 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 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 |
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.