Given a 2D array arr[][], where each element arr[i] is an array of strings representing an account, where the first element arr[i][0] is a name, and the rest of the elements are emails associated with this name. Also, two accounts belong to the same person if there is a common email associated with both accounts. A person can have any number of accounts initially, but all have the same name.
The task is to merge these accounts and print them in the following format: the first element of each account is the name, and the rest of the elements are emails in lexicographically sorted order.
Note: Accounts themselves can be returned in any order. Even if two accounts have the same name, they may belong to different people as different people could have the same name.
Example:
Input: arr[][] =
[["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: There are three accounts with the same name "John", and two of them share a common email which is "johnsmith@mail.com", thus these two accounts are merged, and remaining two will remain unchanged.Input: arr[][] =
[["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: There is no common email in any of the users.
[Approach - 1] Using Depth First Search(DFS)
The idea is to represent the list of accounts arr[][] in the form of Graph, where each node represents an email, and an edge signifies that two emails are connected i.e. they belong to same person. To do so, firstly connect all the emails initially associated to the same name in Star Graph format.
Now explore each connected component to find all the emails that belong to one person using DFS. To do so, iterate over all of the nodes and if the node has already been visited, skip it. Otherwise, perform a DFS traversal over the connected component and store all the visited emails together, as they all belong to one person. Each time we visit an email during a DFS, we will mark it as visited to ensure that we do not search the same connected component more than once.
// C++ program to merge the accounts
#include <bits/stdc++.h>
using namespace std;
// Function to perform DFS
void DFS(vector<string> &account, string &email,
unordered_set<string> &visited,
unordered_map<string, vector<string>> &adjacent) {
// Mark the email as visited
visited.insert(email);
// Add the email vector that contains
// the current component's emails
account.push_back(email);
// perform dfs for connected emails
for (string &x : adjacent[email]) {
if (visited.find(x) == visited.end()) {
DFS(account, x, visited, adjacent);
}
}
}
// Function to merge the email accounts
vector<vector<string>> accountsMerge(vector<vector<string>> &arr) {
unordered_map<string, vector<string>> adjacent;
unordered_set<string> visited;
// Building adjacency list
for (vector<string> &i : arr) {
// Adding edge between first email to
// all other emails in the account
string firstMail = i[1];
for (int j = 2; j < i.size(); j++) {
string email = i[j];
adjacent[firstMail].push_back(email);
adjacent[email].push_back(firstMail);
}
}
// Traverse over all the accounts to store components
vector<vector<string>> accounts;
for (vector<string> &i : arr) {
string accountName = i[0];
string firstMail = i[1];
// If email is visited, then it's a part of different component
// Hence perform DFS only if email is not visited yet
if (visited.find(firstMail) == visited.end()) {
vector<string> account;
// Adding account name at the 0th index
account.push_back(accountName);
DFS(account, firstMail, visited, adjacent);
accounts.push_back(account);
}
}
return accounts;
}
int main() {
vector<vector<string>> arr = {
{"John", "johnsmith@mail.com", "john_newyork@mail.com"},
{"John", "johnsmith@mail.com", "john00@mail.com"},
{"Mary", "mary@mail.com"},
{"John", "johnnybravo@mail.com"}};
arr = accountsMerge(arr);
for (vector<string> &i : arr) {
for (string &j : i) {
cout << j << " ";
}
cout << endl;
}
return 0;
}
// Java program to merge the accounts
import java.util.*;
class GfG {
// Function to perform DFS
private static void DFS(ArrayList<String> account, String email,
Set<String> visited,
Map<String, ArrayList<String>> adjacent) {
// Mark the email as visited
visited.add(email);
// Add the email vector that contains
// the current component's emails
account.add(email);
// Perform DFS for connected emails
for (String x : adjacent.getOrDefault(email, new ArrayList<>())) {
if (!visited.contains(x)) {
DFS(account, x, visited, adjacent);
}
}
}
// Function to merge the email accounts
public static ArrayList<ArrayList<String>> accountsMerge(ArrayList<ArrayList<String>> arr) {
Map<String, ArrayList<String>> adjacent = new HashMap<>();
Set<String> visited = new HashSet<>();
// Building adjacency list
for (ArrayList<String> i : arr) {
// Adding edge between first email to
// all other emails in the account
String firstMail = i.get(1);
for (int j = 2; j < i.size(); j++) {
String email = i.get(j);
adjacent.computeIfAbsent(firstMail, k -> new ArrayList<>()).add(email);
adjacent.computeIfAbsent(email, k -> new ArrayList<>()).add(firstMail);
}
}
// Traverse over all the accounts to store components
ArrayList<ArrayList<String>> accounts = new ArrayList<>();
for (ArrayList<String> i : arr) {
String accountName = i.get(0);
String firstMail = i.get(1);
// If email is visited, then it's a part of different component
// Hence perform DFS only if email is not visited yet
if (!visited.contains(firstMail)) {
ArrayList<String> account = new ArrayList<>();
// Adding account name at the 0th index
account.add(accountName);
DFS(account, firstMail, visited, adjacent);
accounts.add(account);
}
}
return accounts;
}
public static void main(String[] args) {
ArrayList<ArrayList<String>> arr = new ArrayList<>();
arr.add(new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john_newyork@mail.com")));
arr.add(new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john00@mail.com")));
arr.add(new ArrayList<>(Arrays.asList("Mary", "mary@mail.com")));
arr.add(new ArrayList<>(Arrays.asList("John", "johnnybravo@mail.com")));
arr = accountsMerge(arr);
for (ArrayList<String> i : arr) {
for (String j : i) {
System.out.print(j + " ");
}
System.out.println();
}
}
}
# Python program to merge the accounts
from collections import defaultdict
# Function to perform DFS
def DFS(account, email, visited, adjacent):
# Mark the email as visited
visited.add(email)
# Add the email vector that contains
# the current component's emails
account.append(email)
# perform dfs for connected emails
for x in adjacent[email]:
if x not in visited:
DFS(account, x, visited, adjacent)
# Function to merge the email accounts
def accountsMerge(arr):
adjacent = defaultdict(list)
visited = set()
# Building adjacency list
for i in arr:
# Adding edge between first email to
# all other emails in the account
firstMail = i[1]
for j in range(2, len(i)):
email = i[j]
adjacent[firstMail].append(email)
adjacent[email].append(firstMail)
# Traverse over all the accounts to store components
accounts = []
for i in arr:
accountName = i[0]
firstMail = i[1]
# If email is visited, then it's a part of different component
# Hence perform DFS only if email is not visited yet
if firstMail not in visited:
account = []
# Adding account name at the 0th index
account.append(accountName)
DFS(account, firstMail, visited, adjacent)
accounts.append(account)
return accounts
if __name__ == "__main__":
arr = [
["John", "johnsmith@mail.com", "john_newyork@mail.com"],
["John", "johnsmith@mail.com", "john00@mail.com"],
["Mary", "mary@mail.com"],
["John", "johnnybravo@mail.com"]
]
arr = accountsMerge(arr)
for i in arr:
print(" ".join(i))
// C# program to merge the accounts
using System;
using System.Collections.Generic;
class GfG {
// Function to perform DFS
static void DFS(List<string> account, string email,
HashSet<string> visited,
Dictionary<string, List<string>> adjacent) {
// Mark the email as visited
visited.Add(email);
// Add the email vector that contains
// the current component's emails
account.Add(email);
// Perform DFS for connected emails
if (adjacent.ContainsKey(email)) { // Check if email has neighbors
foreach (string x in adjacent[email]) {
if (!visited.Contains(x)) {
DFS(account, x, visited, adjacent);
}
}
}
}
// Function to merge the email accounts
public static List<List<string>> AccountsMerge(List<List<string>> arr) {
var adjacent = new Dictionary<string, List<string>>();
var visited = new HashSet<string>();
// Building adjacency list
foreach (var i in arr) {
// Adding edge between first email to
// all other emails in the account
string firstMail = i[1];
if (!adjacent.ContainsKey(firstMail)) {
adjacent[firstMail] = new List<string>();
}
for (int j = 2; j < i.Count; j++) {
string email = i[j];
if (!adjacent.ContainsKey(email)) {
adjacent[email] = new List<string>();
}
adjacent[firstMail].Add(email);
adjacent[email].Add(firstMail);
}
}
// Traverse over all the accounts to store components
var accounts = new List<List<string>>();
foreach (var i in arr) {
string accountName = i[0];
string firstMail = i[1];
// If email is visited, then it's a part of different component
// Hence perform DFS only if email is not visited yet
if (!visited.Contains(firstMail)) {
var account = new List<string>();
// Adding account name at the 0th index
account.Add(accountName);
DFS(account, firstMail, visited, adjacent);
accounts.Add(account);
}
}
return accounts;
}
static void Main() {
var arr = 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" }
};
var result = AccountsMerge(arr);
foreach (var account in result) {
Console.WriteLine(string.Join(" ", account));
}
}
}
// JavaScript program to merge the accounts
// Function to perform DFS
function DFS(account, email, visited, adjacent) {
// Mark the email as visited
visited.add(email);
// Add the email vector that contains
// the current component's emails
account.push(email);
// Perform DFS for connected emails
if (adjacent[email]) { // Check if email has neighbors
for (let x of adjacent[email]) {
if (!visited.has(x)) {
DFS(account, x, visited, adjacent);
}
}
}
}
// Function to merge the email accounts
function accountsMerge(arr) {
const adjacent = {};
const visited = new Set();
// Building adjacency list
for (const i of arr) {
// Adding edge between first email to
// all other emails in the account
const firstMail = i[1];
if (!adjacent[firstMail]) adjacent[firstMail] = [];
for (let j = 2; j < i.length; j++) {
const email = i[j];
if (!adjacent[email]) adjacent[email] = [];
adjacent[firstMail].push(email);
adjacent[email].push(firstMail);
}
}
// Traverse over all the accounts to store components
const accounts = [];
for (const i of arr) {
const accountName = i[0];
const firstMail = i[1];
// If email is visited, then it's a part of different component
// Hence perform DFS only if email is not visited yet
if (!visited.has(firstMail)) {
const account = [];
// Adding account name at the 0th index
account.push(accountName);
DFS(account, firstMail, visited, adjacent);
accounts.push(account);
}
}
return accounts;
}
// Driver Code
const arr = [
["John", "johnsmith@mail.com", "john_newyork@mail.com"],
["John", "johnsmith@mail.com", "john00@mail.com"],
["Mary", "mary@mail.com"],
["John", "johnnybravo@mail.com"]
];
const result = accountsMerge(arr);
result.forEach(account => console.log(account.join(" ")));
Output
John john00@mail.com john_newyork@mail.com johnsmith@mail.com Mary mary@mail.com John johnnybravo@mail.com
Time Complexity: O(n * k * log(n * k)), where n * k is total number of emails.
Space Complexity: O(n * k), to build an adjacency list.
[Approach - 2] Using Disjoint Set Union
The idea is to iterate through the given accounts, and for each email encountered, either add it to an existing set (if the email is already encountered) or creates a new set. We will use Disjoint Set Union (DSU) to group emails that belong to the same account. After grouping emails, construct the final result by combining the emails within each set with the account name.
// C++ program to merge the arr
#include <bits/stdc++.h>
using namespace std;
// Class to implement Disjoint Set
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 findParent(int node) {
// Find the root of the set to which 'node' belongs
if (node == parent[node]) {
return node;
}
// Make the parent of 'node' the root
return parent[node] = findParent(parent[node]);
}
void unionBySize(int u, int v) {
// Union by size to merge two sets
int par_u = findParent(u);
int par_v = findParent(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];
}
}
};
vector<vector<string>> accountsMerge(vector<vector<string>>& arr) {
unordered_map<string, int> mails;
int n = arr.size();
DisjointSet ds(n);
// Iterate through arr and build disjoint sets
for (int i = 0; i < n; i++) {
for (int j = 1; j < arr[i].size(); j++) {
string mail = arr[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.findParent(it.second);
string mail = it.first;
mergedMails[node].push_back(mail);
}
// Construct the final result
vector<vector<string>> accounts;
for (int i = 0; i < n; i++) {
if (mergedMails[i].size() == 0) {
continue;
}
vector<string> temp;
temp.push_back(arr[i][0]);
for (auto it : mergedMails[i]) {
temp.push_back(it);
}
accounts.push_back(temp);
}
return accounts;
}
int main() {
vector<vector<string>> arr = {
{"John", "johnsmith@mail.com", "john_newyork@mail.com"},
{"John", "johnsmith@mail.com", "john00@mail.com"},
{"Mary", "mary@mail.com"},
{"John", "johnnybravo@mail.com"}};
arr = accountsMerge(arr);
for (vector<string> &i : arr) {
for (string &j : i) {
cout << j << " ";
}
cout << endl;
}
return 0;
}
// Java program to merge the arr
import java.util.*;
class GfG {
// Class to implement Disjoint Set
static class DisjointSet {
ArrayList<Integer> parent, size;
DisjointSet(int n) {
parent = new ArrayList<>(Collections.nCopies(n, 0));
size = new ArrayList<>(Collections.nCopies(n, 1));
for (int i = 0; i < n; i++) {
parent.set(i, i);
}
}
int findParent(int node) {
// Find the root of the set to which 'node' belongs
if (node == parent.get(node)) {
return node;
}
// Make the parent of 'node' the root
parent.set(node, findParent(parent.get(node)));
return parent.get(node);
}
void unionBySize(int u, int v) {
// Union by size to merge two sets
int par_u = findParent(u);
int par_v = findParent(v);
if (par_u == par_v) {
return;
}
if (size.get(par_u) < size.get(par_v)) {
parent.set(par_u, par_v);
size.set(par_v, size.get(par_v) + size.get(par_u));
} else {
parent.set(par_v, par_u);
size.set(par_u, size.get(par_u) + size.get(par_v));
}
}
}
static ArrayList<ArrayList<String>> accountsMerge(ArrayList<ArrayList<String>> arr) {
HashMap<String, Integer> mails = new HashMap<>();
int n = arr.size();
DisjointSet ds = new DisjointSet(n);
// Iterate through arr and build disjoint sets
for (int i = 0; i < n; i++) {
for (int j = 1; j < arr.get(i).size(); j++) {
String mail = arr.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
ArrayList<ArrayList<String>> mergedMails = new ArrayList<>(Collections.nCopies(n, null));
for (int i = 0; i < n; i++) {
mergedMails.set(i, new ArrayList<>());
}
for (Map.Entry<String, Integer> it : mails.entrySet()) {
int node = ds.findParent(it.getValue());
String mail = it.getKey();
mergedMails.get(node).add(mail);
}
// Construct the final result
ArrayList<ArrayList<String>> accounts = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (mergedMails.get(i).isEmpty()) {
continue;
}
ArrayList<String> temp = new ArrayList<>();
temp.add(arr.get(i).get(0));
temp.addAll(mergedMails.get(i));
accounts.add(temp);
}
return accounts;
}
public static void main(String[] args) {
ArrayList<ArrayList<String>> arr = new ArrayList<>(Arrays.asList(
new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john_newyork@mail.com")),
new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john00@mail.com")),
new ArrayList<>(Arrays.asList("Mary", "mary@mail.com")),
new ArrayList<>(Arrays.asList("John", "johnnybravo@mail.com"))
));
ArrayList<ArrayList<String>> result = accountsMerge(arr);
for (ArrayList<String> i : result) {
System.out.println(String.join(" ", i));
}
}
}
# Python program to merge the arr
class DisjointSet:
def __init__(self, n):
self.parent = [i for i in range(n)]
self.size = [1] * n
def findParent(self, node):
# Find the root of the set to which 'node' belongs
if node == self.parent[node]:
return node
# Make the parent of 'node' the root
self.parent[node] = self.findParent(self.parent[node])
return self.parent[node]
def unionBySize(self, u, v):
# Union by size to merge two sets
par_u = self.findParent(u)
par_v = self.findParent(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]
def accountsMerge(arr):
mails = {}
n = len(arr)
ds = DisjointSet(n)
# Iterate through arr and build disjoint sets
for i in range(n):
for j in range(1, len(arr[i])):
mail = arr[i][j]
if mail not in mails:
mails[mail] = i
else:
ds.unionBySize(i, mails[mail])
# Group merged emails based on disjoint sets
mergedMails = [[] for _ in range(n)]
for mail, index in mails.items():
node = ds.findParent(index)
mergedMails[node].append(mail)
# Construct the final result
accounts = []
for i in range(n):
if not mergedMails[i]:
continue
temp = [arr[i][0]]
temp.extend(mergedMails[i])
accounts.append(temp)
return accounts
if __name__ == "__main__":
arr = [
["John", "johnsmith@mail.com", "john_newyork@mail.com"],
["John", "johnsmith@mail.com", "john00@mail.com"],
["Mary", "mary@mail.com"],
["John", "johnnybravo@mail.com"]
]
result = accountsMerge(arr)
for i in result:
print(" ".join(i))
// C# program to merge the arr
using System;
using System.Collections.Generic;
class GfG {
// Class to implement Disjoint Set
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 findParent(int node) {
if (node == parent[node]) {
return node;
}
// Path compression
return parent[node] = findParent(parent[node]);
}
public void unionBySize(int u, int v) {
int par_u = findParent(u);
int par_v = findParent(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 static List<List<string>> AccountsMerge(List<List<string>> arr) {
var mails = new Dictionary<string, int>();
int n = arr.Count;
var ds = new DisjointSet(n);
// Iterate through arr and build disjoint sets
for (int i = 0; i < n; i++) {
for (int j = 1; j < arr[i].Count; j++) {
string mail = arr[i][j];
if (!mails.ContainsKey(mail)) {
mails[mail] = i;
} else {
ds.unionBySize(i, mails[mail]);
}
}
}
// Group merged emails based on disjoint sets
var mergedMails = new List<string>[n]; // Array of lists
for (int i = 0; i < n; i++) {
mergedMails[i] = new List<string>();
}
foreach (var it in mails) {
int node = ds.findParent(it.Value);
string mail = it.Key;
mergedMails[node].Add(mail);
}
// Construct the final result
var accounts = new List<List<string>>();
for (int i = 0; i < n; i++) {
if (mergedMails[i].Count == 0) {
continue;
}
var temp = new List<string>();
temp.Add(arr[i][0]); // Add account name
temp.AddRange(mergedMails[i]);
accounts.Add(temp);
}
return accounts;
}
public static void Main(string[] args) {
var arr = 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" }
};
var result = AccountsMerge(arr);
foreach (var i in result) {
Console.WriteLine(string.Join(" ", i));
}
}
}
// JavaScript program to merge the arr
class DisjointSet {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
this.size = Array(n).fill(1);
}
findParent(node) {
// Find the root of the set to which 'node' belongs
if (node === this.parent[node]) {
return node;
}
// Make the parent of 'node' the root
return (this.parent[node] = this.findParent(this.parent[node]));
}
unionBySize(u, v) {
// Union by size to merge two sets
const par_u = this.findParent(u);
const par_v = this.findParent(v);
if (par_u === par_v) {
return;
}
if (this.size[par_u] < this.size[par_v]) {
this.parent[par_u] = par_v;
this.size[par_v] += this.size[par_u];
} else {
this.parent[par_v] = par_u;
this.size[par_u] += this.size[par_v];
}
}
}
function accountsMerge(arr) {
const mails = new Map();
const n = arr.length;
const ds = new DisjointSet(n);
// Iterate through arr and build disjoint sets
for (let i = 0; i < n; i++) {
for (let j = 1; j < arr[i].length; j++) {
const mail = arr[i][j];
if (!mails.has(mail)) {
mails.set(mail, i);
} else {
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.entries()) {
const node = ds.findParent(index);
mergedMails[node].push(mail);
}
// Construct the final result
const accounts = [];
for (let i = 0; i < n; i++) {
if (mergedMails[i].length === 0) {
continue;
}
const temp = [arr[i][0]];
accounts.push(temp.concat(mergedMails[i]));
}
return accounts;
}
// Main function
const arr = [
["John", "johnsmith@mail.com", "john_newyork@mail.com"],
["John", "johnsmith@mail.com", "john00@mail.com"],
["Mary", "mary@mail.com"],
["John", "johnnybravo@mail.com"]
];
const result = accountsMerge(arr);
result.forEach(i => console.log(i.join(" ")));
Output
John john00@mail.com john_newyork@mail.com johnsmith@mail.com Mary mary@mail.com John johnnybravo@mail.com
Time Complexity: O(n * k * log(n * k)), where n * k is total number of emails.
Space Complexity: O(n * k), to build an adjacency list.