Group Anagrams
Given an array of strings strs, group the anagrams together. You can return the answer in any order.
- 1 <= strs.length <= 10⁴
- 0 <= strs[i].length <= 100
- strs[i] consists of lowercase English letters only
strs = ["eat","tea","tan","ate","nat","bat"][["bat"],["nat","tan"],["ate","eat","tea"]]strs = [""][[""]]strs = ["a"][["a"]]We are given a list of words and must gather them into groups, where each group holds words that are anagrams of one another — same letters, same counts, order irrelevant — like "eat", "tea", "ate". The groups may come back in any order, and the words within a group in any order; all that matters is the partition. This is the plural of Valid Anagram: there we answered "are these two words anagrams?", here we must sort a whole list into anagram families at once.
The direct extension of Valid Anagram: keep a list of groups, and for each new word walk the existing groups, run the two-word anagram check against a representative of each, and drop the word into the first group it matches — or start a new group if none do. Correct, but count it. With N words, a word may be compared against up to N existing representatives, and each anagram check scans about K characters (K the word length). That is O(N² × K), and the wasted effort is the repeated pairwise testing: we keep re-establishing "these two are anagrams" from scratch, one pair at a time, when belonging to a family is really a property of the single word.
The fix reuses the exact insight from Valid Anagram — an anagram is defined by its letter census, not its arrangement. So give every word a canonical key: a fingerprint that is identical for all anagrams of the word and different for everything else. If two words share the key, they are anagrams, no pairwise comparison needed. A single hash map keyed by that fingerprint then drops each word straight into its family in one pass — the same "spend memory to skip repeated scanning" trade behind Two Sum, applied to grouping.
Two natural fingerprints:
- Sorted letters. Sorting "eat", "tea", and "ate" all yields "aet". Cost: O(K log K) per word to sort.
- Letter counts. A 26-number tally — the very tally sheet from Valid Anagram — encoded into a string is identical for anagrams. Cost: O(K) per word, no sort.
Walk the list once. Compute each word's key and append the word to the map entry under that key. At the end, the map's values are the groups.
from collections import defaultdict
def group_anagrams(strs):
groups = defaultdict(list)
for word in strs:
key = "".join(sorted(word)) # canonical: anagrams share it
groups[key].append(word)
return list(groups.values())One pass builds the answer: O(N × K log K) with the sorted key, or O(N × K) with the count key — either way a decisive win over the O(N² × K) pairwise approach, paid for with O(N × K) space to hold the map. The reflex worth internalizing reaches far past anagrams: whenever you must group items by an equivalence relation ("same when reordered", "same when normalized", "same shape"), do not compare pairs — invent a canonical form that is identical exactly for equivalent items, and hash on it. Finding that canonical form is the whole game; here it was the letter census you already met in Valid Anagram.
anagram grouping
Ready to group anagrams from the input list.