Given an array of words arr[], the task is to groups strings that are anagrams. An anagram is a word or phrase formed by rearranging the letters of another, using all the original letters exactly once.
Example:
Input: arr[] = ["act", "god", "cat", "dog", "tac"]
Output: [["act", "cat", "tac"], ["god", "dog"]]
Explanation: There are 2 groups of anagrams "god", "dog" make group 1. "act", "cat", "tac" make group 2.Input: arr[] = ["listen", "silent", "enlist", "abc", "cab", "bac", "rat", "tar", "art"]
Output: [["abc", "cab", "bac"], ["listen", "silent", "enlist"],["rat", "tar", "art"]]
Explanation:
Group 1: "abc", "bac" and "cab" are anagrams.
Group 2: "listen", "silent" and "enlist" are anagrams.
Group 3: "rat", "tar" and "art" are anagrams.
Table of Content
[Naive Approach] Using sorted words as keys - O(n*k*log(k)) Time and O(n*k) Space
The idea is that if we sort two strings which are anagrams of each other, then the sorted strings will always be the same. So, we can maintain a hash map with the sorted strings as keys and the index of the anagram group in the result array as the value.
To know more about the implementation, please refer to Group Anagrams Together Using Sorting.
[Expected Approach] Using Frequency as keys - O(n*k) Time and O(n*k) Space
The idea is to that if two strings are anagrams of each other, then the frequency of all characters in both strings will always be the same. So, we can maintain a hash map with the count of characters as keys and the index of the anagram group in the result array as the value. For each word, we can first construct a frequency array of size 26 to store the frequency of each character in the word. Then, we can append the frequency of each character separated by a delimiter, say '$' to form the key for hash map.
Note the MAX_CHAR is alphabet size of input characters which is typically a constant. If we have only lower case characters, then MAX_CHAR is 26 only. If we consider all ASCII characters, then MAX_CHAR is 256.
// C++ Code to group anagrams together by using frequency
// as keys
#include <bits/stdc++.h>
using namespace std;
const int MAX_CHAR = 26;
// function to generate hash of word s
string getHash(string &s) {
string hash;
vector<int> freq(MAX_CHAR, 0);
// Count frequency of each character
for(char ch: s)
freq[ch - 'a'] += 1;
// Append the frequency to construct the hash
for(int i = 0; i < MAX_CHAR; i++) {
hash.append(to_string(freq[i]));
hash.append("$");
}
return hash;
}
vector<vector<string>> anagrams(vector<string> &arr) {
vector<vector<string>> res;
unordered_map<string, int> mp;
for (int i = 0; i < arr.size(); i++) {
string key = getHash(arr[i]);
// If key is not present in the hash map, add
// an empty group (vector) in the result and
// store the index of the group in hash map
if (mp.find(key) == mp.end()) {
mp[key] = res.size();
res.push_back({});
}
// Insert the string in its correct group
res[mp[key]].push_back(arr[i]);
}
return res;
}
int main() {
vector<string> arr = {"act", "god", "cat", "dog", "tac"};
vector<vector<string>> res = anagrams(arr);
for(int i = 0; i < res.size(); i++) {
for(int j = 0; j < res[i].size(); j++)
cout << res[i][j] << " ";
cout << "\n";
}
return 0;
}
// Java Code to group anagrams together by using frequency
// as keys
import java.util.*;
class GfG {
static final int MAX_CHAR = 26;
// Function to generate hash of word s
static String getHash(String s) {
StringBuilder hash = new StringBuilder();
int[] freq = new int[MAX_CHAR];
// Count frequency of each character
for (char ch : s.toCharArray()) {
freq[ch - 'a']++;
}
// Append the frequency to construct the hash
for (int i = 0; i < MAX_CHAR; i++) {
hash.append(freq[i]);
hash.append("$");
}
return hash.toString();
}
static ArrayList<ArrayList<String>> anagrams(String[] arr) {
ArrayList<ArrayList<String>> res = new ArrayList<>();
Map<String, Integer> mp = new HashMap<>();
for (int i = 0; i < arr.length; i++) {
String key = getHash(arr[i]);
// If key is not present in the hash map, add
// an empty group (List) in the result and
// store the index of the group in hash map
if (!mp.containsKey(key)) {
mp.put(key, res.size());
res.add(new ArrayList<>());
}
// Insert the string in its correct group
res.get(mp.get(key)).add(arr[i]);
}
return res;
}
public static void main(String[] args) {
String[] arr = {"act", "god", "cat", "dog", "tac"};
ArrayList<ArrayList<String>> res = anagrams(arr);
for (List<String> group : res) {
for (String word : group) {
System.out.print(word + " ");
}
System.out.println();
}
}
}
# Python Code to group anagrams together by using frequency
# as keys
MAX_CHAR = 26
# function to generate hash of word s
def getHash(s):
hashList = []
freq = [0] * MAX_CHAR
# Count frequency of each character
for ch in s:
freq[ord(ch) - ord('a')] += 1
# Append the frequency to construct the hash
for i in range(MAX_CHAR):
hashList.append(str(freq[i]))
hashList.append("$")
return ''.join(hashList)
def anagrams(arr):
res = []
mp = {}
for i in range(len(arr)):
key = getHash(arr[i])
# If key is not present in the hash map, add
# an empty group (list) in the result and
# store the index of the group in hash map
if key not in mp:
mp[key] = len(res)
res.append([])
# Insert the string in its correct group
res[mp[key]].append(arr[i])
return res
if __name__ == "__main__":
arr = ["act", "god", "cat", "dog", "tac"]
res = anagrams(arr)
for group in res:
for word in group:
print(word, end=" ")
print()
using System;
using System.Collections.Generic;
using System.Text;
class GfG {
const int MAX_CHAR = 26;
// Function to generate hash of word s
static string GetHash(string s) {
StringBuilder hash = new StringBuilder();
int[] freq = new int[MAX_CHAR];
// Count frequency of each character
foreach (char ch in s) {
freq[ch - 'a'] += 1;
}
// Append the frequency to construct the hash
for (int i = 0; i < MAX_CHAR; i++) {
hash.Append(freq[i].ToString());
hash.Append("$");
}
return hash.ToString();
}
static List<List<string>> Anagrams(string[] arr) {
List<List<string>> res = new List<List<string>>();
Dictionary<string, int> mp = new Dictionary<string, int>();
for (int i = 0; i < arr.Length; i++) {
string key = GetHash(arr[i]);
// If key is not present in the hash map, add
// an empty group (List) in the result and
// store the index of the group in hash map
if (!mp.ContainsKey(key)) {
mp[key] = res.Count;
res.Add(new List<string>());
}
// Insert the string in its correct group
res[mp[key]].Add(arr[i]);
}
return res;
}
static void Main() {
string[] arr = { "act", "god", "cat", "dog", "tac" };
List<List<string>> res = Anagrams(arr);
foreach (var group in res) {
foreach (var word in group) {
Console.Write(word + " ");
}
Console.WriteLine();
}
}
}
// JavaScript Code to group anagrams together by using frequency
// as keys
const MAX_CHAR = 26;
// Function to generate hash of word s
function getHash(s) {
let freq = Array(MAX_CHAR).fill(0);
// Count frequency of each character
for (let i = 0; i < s.length; i++) {
let ch = s[i];
freq[ch.charCodeAt(0) - 'a'.charCodeAt(0)] += 1;
}
// Create hash string using join to avoid string concatenation in the loop
let hashArray = [];
for (let i = 0; i < MAX_CHAR; i++) {
hashArray.push(freq[i].toString());
hashArray.push('$');
}
return hashArray.join('');
}
function anagrams(arr) {
let res = [];
let mp = new Map();
for (let i = 0; i < arr.length; i++) {
let key = getHash(arr[i]);
// If key is not present in the hash map, add
// an empty group (array) in the result and
// store the index of the group in hash map
if (!mp.has(key)) {
mp.set(key, res.length);
res.push([]);
}
// Insert the string in its correct group
res[mp.get(key)].push(arr[i]);
}
return res;
}
// Driver Code
let arr = ["act", "god", "cat", "dog", "tac"];
let res = anagrams(arr);
for (let i = 0; i < res.length; i++) {
let temp = '';
for (let j = 0; j < res[i].length; j++) {
temp += res[i][j] + ' ';
}
console.log(temp);
}
Output
act cat tac god dog
Time Complexity: O(n*k), where n is the number of words and k is the maximum length of a word.
Auxiliary Space: O(n*k)