BackmediumStringsGoogleAmazon

Group Anagrams Solution

Problem Statement

You are provided with an array of lowercase alphabetic strings. Two strings are considered anagrams if one can be rearranged to form the other, meaning they share the exact same character frequencies. Your task is to partition the input array into groups where every string within a specific group is an anagram of every other string in that group.

Return a list of lists containing the grouped anagrams. The order of the groups in the final output does not matter, nor does the order of the strings within each individual group. However, the relative order of strings that are not anagrams of each other should generally be preserved in standard implementations, though the problem specification only requires correct grouping.

For example, if the input contains "eat", "tea", and "ate", they all consist of one 'a', one 'e', and one 't', so they must appear in the same sublist. If the input contains "ant" and "tan", they also form a group. Strings that do not share the same character frequency profile with any other string in the array will form their own singleton groups.

Example 1
Input
strs = ["cab", "abc", "bca", "xyz", "zyx"]
Output
[["cab", "abc", "bca"], ["xyz", "zyx"]]

Explanation: 1. Analyze "cab": frequencies are {a:1, b:1, c:1}. 2. Analyze "abc": frequencies are {a:1, b:1, c:1}. This matches "cab", so they are grouped. 3. Analyze "bca": frequencies are {a:1, b:1, c:1}. This matches the previous group. 4. Analyze "xyz": frequencies are {x:1, y:1, z:1}. No previous string matches this profile, so a new group is started. 5. Analyze "zyx": frequencies are {x:1, y:1, z:1}. This matches "xyz", so they are grouped together. Final groups: [["cab", "abc", "bca"], ["xyz", "zyx"]].

Example 2
Input
strs = ["listen", "silent", "enlist", "hello", "world"]
Output
[["listen", "silent", "enlist"], ["hello"], ["world"]]

Explanation: 1. "listen" has frequencies {e:1, i:1, l:1, n:1, s:1, t:1}. 2. "silent" has the same frequencies, so it joins the first group. 3. "enlist" has the same frequencies, joining the first group. 4. "hello" has frequencies {e:1, h:1, l:2, o:1}. No match found, so it forms a singleton group. 5. "world" has frequencies {d:1, l:1, o:1, r:1, w:1}. No match found, so it forms a singleton group. Final output contains three sublists.

Example 3
Input
strs = ["a", "b", "a", "b", "c"]
Output
[["a", "a"], ["b", "b"], ["c"]]

Explanation: 1. "a" has frequency {a:1}. 2. "b" has frequency {b:1}. New group. 3. "a" has frequency {a:1}. Matches the first group. 4. "b" has frequency {b:1}. Matches the second group. 5. "c" has frequency {c:1}. New group. The output groups identical characters together. Note: Since 'a' and 'b' are single characters, they are only anagrams of themselves.

Constraints

  • 1 <= strs.length <= 10^4
  • 1 <= strs[i].length <= 100
  • strs[i] consists of lowercase English letters only.
Live Compiler1 Free Run Available
Loading Editor...
Test Cases & Output
Click "Run" to test your 1 free compile trial!

🚀 Practice this problem

Run code, get AI hints & track streak

Sign Up Free

Group Anagrams — Problem Statement & Solution Guide

StringsMediumHash Maps
TimeO(n * k)
|
SpaceO(n * k)

Problem Description

You are provided with an array of lowercase alphabetic strings. Two strings are considered anagrams if one can be rearranged to form the other, meaning they share the exact same character frequencies. Your task is to partition the input array into groups where every string within a specific group is an anagram of every other string in that group.

Return a list of lists containing the grouped anagrams. The order of the groups in the final output does not matter, nor does the order of the strings within each individual group. However, the relative order of strings that are not anagrams of each other should generally be preserved in standard implementations, though the problem specification only requires correct grouping.

For example, if the input contains "eat", "tea", and "ate", they all consist of one 'a', one 'e', and one 't', so they must appear in the same sublist. If the input contains "ant" and "tan", they also form a group. Strings that do not share the same character frequency profile with any other string in the array will form their own singleton groups.

DSA Pattern Breakdown

DSA Pattern Breakdown

"Group Anagrams"

medium

WHY DOES IT MATTER?

Hash‑based grouping transforms an O(n^2) comparison problem into a single pass O(n) operation, which is essential for real‑time systems that must process large volumes of data without latency spikes. It also provides a clean separation between data representation (the key) and grouping logic, making the algorithm modular and testable.

OPTIMIZATION CHALLENGE

The crux is choosing a key that is both collision‑free for anagrams and inexpensive to compute. Using a sorted string is simple but incurs O(k log k) cost; using a 26‑element frequency array reduces this to O(k) and eliminates sorting, which is the key insight that brings the algorithm into linear time.

REAL-WORLD CONNECTION

Think of a distributed log aggregation system where log entries are grouped by a hash of their content to detect duplicates or similar patterns. Just as logs are bucketed by hash to enable quick lookups and deduplication, anagram strings are bucketed by their frequency signature to enable instant grouping.

When explaining this in an interview, emphasize the trade‑off between simplicity and performance: start with the sorted string for clarity, then discuss how a frequency array or hash of counts scales better for large inputs. Also, mention that the choice of data structure (list vs. tuple vs. string) can affect both time and space overhead.

COMPLEXITY AT A GLANCE

⏱ Time:O(n * k)
đź’ľ Space:O(n * k)

Core Theory — Why This Approach?

The optimal solution for grouping anagrams relies on the fact that two strings are anagrams if and only if they have identical character frequency distributions. A naive approach would compare each pair of strings, leading to an O(n^2 * k) time complexity where n is the number of strings and k is the average string length—impractical for large inputs. The efficient paradigm uses a hash map (dictionary) where the key is a canonical representation of the character counts. Two common canonical forms are: 1) the sorted string (e.g., "eat" → "aet"), which costs O(k log k) per string, or 2) a 26‑element frequency tuple (e.g., "eat" → (1,0,0,…,1,0,1,…)), which can be built in O(k) time. By iterating once over the input array, computing the key, and appending the original string to the corresponding bucket, we achieve linear‑time grouping with linear auxiliary space. This pattern is a classic example of the “hash‑based grouping” technique, widely used in problems involving equivalence classes, such as grouping words by anagrams, palindromic partitions, or even clustering user sessions by behavior signatures.

Interview Questions on This Problem

Q1How would you modify the grouping algorithm to handle Unicode strings with arbitrary alphabets, such as emojis or non‑Latin characters?

For Unicode, the sorted‑string approach still works because sorting is defined over code points, but it becomes expensive for long strings. A more scalable method is to use a dictionary that maps each unique character to a count, then serialize the dictionary into a sorted tuple of (char, count) pairs. This preserves the frequency signature regardless of alphabet size and avoids the O(k log k) sorting cost. In practice, you would use a Counter or defaultdict and then convert to a frozenset or tuple for hashing.

Q2In a fintech platform, you need to detect duplicate transaction descriptions that are anagrams of each other. What considerations would you add to the algorithm to ensure it scales to millions of records?

For millions of records, memory becomes a bottleneck. You can stream the data and use a disk‑based key‑value store (e.g., RocksDB) to persist buckets. Additionally, you can hash the frequency tuple to a 64‑bit integer to reduce key size, and use a Bloom filter to quickly skip strings that cannot form anagrams with any existing bucket. Parallelizing the key computation across CPU cores and sharding the hash map by the first character can also improve throughput.

Q3A high‑growth startup asks you to explain how you would test the correctness of your anagram grouping implementation under edge cases. What test cases would you include?

Key test cases: 1) Empty string array and array with a single empty string; 2) Strings of varying lengths where some are anagrams and others are not; 3) Strings containing repeated characters (e.g., "aaa", "aa"); 4) Very long strings (e.g., 10,000 characters) to test performance; 5) Strings with Unicode characters and mixed case; 6) Duplicate strings to ensure they end up in the same group; 7) Randomly generated strings to test average‑case behavior. Each case should assert that the output groups contain exactly the expected strings and that no string is omitted or duplicated.

Examples

Example 1

Input

strs = ["cab", "abc", "bca", "xyz", "zyx"]

Output

[["cab", "abc", "bca"], ["xyz", "zyx"]]

Explanation: 1. Analyze "cab": frequencies are {a:1, b:1, c:1}. 2. Analyze "abc": frequencies are {a:1, b:1, c:1}. This matches "cab", so they are grouped. 3. Analyze "bca": frequencies are {a:1, b:1, c:1}. This matches the previous group. 4. Analyze "xyz": frequencies are {x:1, y:1, z:1}. No previous string matches this profile, so a new group is started. 5. Analyze "zyx": frequencies are {x:1, y:1, z:1}. This matches "xyz", so they are grouped together. Final groups: [["cab", "abc", "bca"], ["xyz", "zyx"]].

Example 2

Input

strs = ["listen", "silent", "enlist", "hello", "world"]

Output

[["listen", "silent", "enlist"], ["hello"], ["world"]]

Explanation: 1. "listen" has frequencies {e:1, i:1, l:1, n:1, s:1, t:1}. 2. "silent" has the same frequencies, so it joins the first group. 3. "enlist" has the same frequencies, joining the first group. 4. "hello" has frequencies {e:1, h:1, l:2, o:1}. No match found, so it forms a singleton group. 5. "world" has frequencies {d:1, l:1, o:1, r:1, w:1}. No match found, so it forms a singleton group. Final output contains three sublists.

Example 3

Input

strs = ["a", "b", "a", "b", "c"]

Output

[["a", "a"], ["b", "b"], ["c"]]

Explanation: 1. "a" has frequency {a:1}. 2. "b" has frequency {b:1}. New group. 3. "a" has frequency {a:1}. Matches the first group. 4. "b" has frequency {b:1}. Matches the second group. 5. "c" has frequency {c:1}. New group. The output groups identical characters together. Note: Since 'a' and 'b' are single characters, they are only anagrams of themselves.

Constraints

  • 1 <= strs.length <= 10^4
  • 1 <= strs[i].length <= 100
  • strs[i] consists of lowercase English letters only.

Optimal Approach & Strategy

Compute a canonical key (sorted string or frequency tuple) for each string in O(k) time, insert the string into a hash map keyed by that signature, and collect the groups. This achieves O(n * k) time and O(n * k) space.

Brute Force Approach

Compare every pair of strings to check if they are anagrams, and if so, merge them into the same group. This requires O(n^2 * k) time and O(1) extra space, making it infeasible for large inputs.

Verified Code Solutions

JavaScript Solution
Time: O(n * k)
function groupAnagrams(strs) { 
       const anagrams = {}; 
       for (let word of strs) { 
           const sortedWord = word.split('').sort().join(''); 
           if (!anagrams[sortedWord]) { 
               anagrams[sortedWord] = []; 
           } 
           anagrams[sortedWord].push(word); 
       } 
       return Object.values(anagrams); 
   }

Asked in Top Tech Interviews

GoogleAmazonMeta

Solve in Interative Editor

Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.