AlgoMaster Logo

Group Anagrams

mediumFrequency7 min readUpdated June 23, 2026

Understanding the Problem

Two strings are anagrams if they contain exactly the same characters with exactly the same frequencies. "eat" and "tea" are anagrams because both have one 'e', one 'a', and one 't'. Order doesn't matter, only the character composition does.

The question becomes how to create a "fingerprint" for each string such that all anagrams share the same fingerprint and no two non-anagrams collide. Given that, we group strings by their fingerprint using a hash map.

This is a grouping problem, and the challenge is choosing the hash map key.

Key Constraints:

  • 1 <= strs.length <= 10^4 → Up to 10,000 strings. Comparing every string against every other would be O(n^2) string comparisons. Grouping by a hash key avoids that and processes each string once.
  • 0 <= strs[i].length <= 100 → Strings are at most 100 characters, so sorting a single string is cheap and building a 26-slot frequency array is constant work. Strings can also be empty, and the empty string is its own valid group.
  • lowercase English letters only → Only 26 possible characters, so a fixed-size array of 26 counts captures everything a key needs to distinguish.

Approach 1: Sorting

Intuition

If two strings are anagrams, they contain the same characters, so sorting both alphabetically produces the same string. "eat" sorts to "aet", "tea" sorts to "aet", and "ate" sorts to "aet". That sorted form is a canonical fingerprint shared by every anagram and by nothing else: a non-anagram differs in at least one character, so its sorted form differs too.

For each string, sort its characters to produce a key, then use a hash map to group all strings that share the same key.

Algorithm

  1. Create a hash map where the key is a sorted string and the value is a list of original strings.
  2. For each string in the input array:
    • Sort the characters of the string to produce a key.
    • Add the original string to the list associated with that key in the hash map.
  3. Return all the lists from the hash map as the result.

Example Walkthrough

1Initialize: empty hash map
0
eat
1
tea
2
tan
3
ate
4
nat
5
bat
1/7

Code

Sorting is the bottleneck: every string costs O(k log k) to produce a key. Since the input only contains lowercase English letters, the next approach builds the key from a character count in O(k) time instead of sorting.

Approach 2: Character Frequency Count (Optimal)

Intuition

Since the input only contains lowercase English letters, there are exactly 26 possible characters. Instead of sorting a string into canonical form, count how many times each character appears and use that frequency count as the key.

For "eat": a=1, e=1, t=1. For "tea": a=1, e=1, t=1. Same frequency count, same key.

The frequency array then has to become a hashable key. One way is to join the counts with a delimiter, like #1#0#0#0#1#0#0...#1#0#0..., where each number is the count for a, b, c, ..., z. This produces one string per distinct frequency pattern, avoids sorting, and brings the per-string cost down from O(k log k) to O(k).

Algorithm

  1. Create a hash map where the key is a frequency-encoded string and the value is a list of original strings.
  2. For each string in the input array:
    • Create an array of size 26 (initialized to zeros) to count character frequencies.
    • Iterate through the string, incrementing the count for each character.
    • Convert the frequency array into a string key (e.g., join counts with a delimiter).
    • Add the original string to the list associated with that key in the hash map.
  3. Return all the lists from the hash map as the result.

Example Walkthrough

1Initialize: empty hash map, process each string
0
eat
1
tea
2
tan
3
ate
4
nat
5
bat
1/7

Code

The frequency count approach is optimal in time: you cannot do better than O(n &middot; k), since every character of every string must be read at least once. A different way to build the key uses prime numbers to produce a single number instead of a string. It is instructive, but it has an overflow limitation that the frequency-count approach does not, covered below.

Approach 3: Prime Number Hashing (Alternative)

Intuition

Assign each letter a unique prime number: a=2, b=3, c=5, d=7, e=11, and so on. For each string, multiply the primes for its characters. By the fundamental theorem of arithmetic, every integer has one prime factorization, so two strings produce the same product if and only if they contain the same characters with the same frequencies.

For "eat": 11 &middot; 2 &middot; 71 = 1562. For "tea": 71 &middot; 11 &middot; 2 = 1562, the same product. For "bat": 3 &middot; 2 &middot; 71 = 426, a different product.

This is a numeric key with no string building, no sorting, and no delimiter. The cost is overflow: the product grows multiplicatively, so with the constraint of strings up to 100 characters it exceeds a 64-bit integer (a string of 100 z's would be 101^100). The fixed-width integer versions below wrap around on overflow without any error and can place non-anagrams in the same group, so Approach 2 is the safe default. The JavaScript and TypeScript versions use arbitrary-precision BigInt, so they stay correct at the cost of slower multiplication.

Algorithm

  1. Define a mapping from each letter (a-z) to a unique prime number.
  2. Create a hash map where the key is a long integer (the prime product) and the value is a list of original strings.
  3. For each string in the input array:
    • Compute the product of primes for each character.
    • Add the original string to the list associated with that product in the hash map.
  4. Return all the lists from the hash map as the result.

Example Walkthrough

1Primes: a=2, b=3, e=11, n=47, t=71. Compute product for each string
0
eat
1
tea
2
tan
3
ate
4
nat
5
bat
1/7

Code