See all coding puzzles and patterns here.
  • Difficulty: Medium

Problem Definition

Given a list of strings, group the list into sets of words that are anagrams of each other.

Assume that you already have a function that checks if two strings are anagrams and that it has a space complexity $O(n)$ and a runtime complexity $O(n)$, where $n$ is the length of the larger of the two words. You can also find an implementation of such a function in Anagram Check.


Examples

Some test cases to verify our solution.

inp = ["tea", "ate", "eat", "bat", "eta", "tab", "rot", "hit"]
expected = [{"tea", "ate", "eat", "eta"}, {"tab", "bat"}, {"rot"}, {"hit"}]
result = anagram_groups(inp)
for exp in expected:
    assert exp in result

inp = ["tea", "bat", "rot", "hit"]
expected = [{"tea"}, {"bat"}, {"rot"}, {"hit"}]
result = anagram_groups(inp)
for exp in expected:
    assert exp in result

Solution

from collections import defaultdict

def anagram_groups(arr):
    processed = set()
    groups = defaultdict()
    n = len(arr)
    for i in range(n):
        item_1 = arr[i]
        if item_1 not in processed:
            processed.add(item_1)
            groups[item_1].add(item_1)
            for j in range(i, n):
                item_2 = arr[j]
                if item_2 not in processed and are_anagrams(item_1, item_2):
                    processed.add(item_2)
                    groups[item_1].add(item_2)
    return [list(groups[leader]) for leader in groups]

Complexity

Let $n = $ len(arr), $m = $ max(len(x) for x in arr).

Runtime complexity should be $O(n^2 \cdot m)$.

Auxiliary space complexity should be $O(n \cdot m)$.