LC #49: Group Anagrams

LilSardarXLilSardarX
4 min read

๐Ÿง  Problem Statement

Given an array of strings, group the anagrams together. Return the grouped anagrams as a list of lists.

Group Anagrams โ€“ Leetcode #49


๐Ÿ’ก Intuition

Anagrams share the same characters. If you sort their letters, they become identical. So, we can bucket words based on their sorted version.


๐Ÿ’ฅ Brute Force (Don't do this)

  • Compare each string with every other using isAnagram(s, t) (a utility method inspired from Valid Anagram solution)
  • And store string accordingly in a list of lists
  • Too slow for large inputs โ†’ O(nยฒ * k)

โšก Optimized Approach: Sorting as Key

  • โœ… Sort the characters of each word
  • โœ… Use the sorted string as a key in a HashMap
  • โœ… Group words into the list mapped to that key
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {

        Map<String, List<String>> map = new HashMap<>();

        for (String s : strs) {
            char[] arr = s.toCharArray();
            Arrays.sort(arr);
            String key = new String(arr);

            if (!map.containsKey(key)) {
                map.put(key, new ArrayList<>());
            }
            map.get(key).add(s);
        }

        return new ArrayList<>(map.values());
    }
}
  • Time: O(n * k log k) โ†’ n = num of words, k = avg word length
  • Space: O(n * k) โ†’ for storing map buckets

โšก Even More Optimized (But Not As Readable) Approach

Instead of sorting each word (O(k log k)), we can use a faster trick:

  • โœ… Count the frequency of each character using int[26]
  • โœ… Convert this frequency array into a string key
  • โœ… Group words in the map using that key
for (String s : strs) {
    int[] count = new int[26];
    for (char c : s.toCharArray()) {
        count[c - 'a']++;
    }

    StringBuilder sb = new StringBuilder();
    for (int freq : count) {
        sb.append(freq).append('#');
    }

    String key = sb.toString();

    if (!map.containsKey(key)) {
        map.put(key, new ArrayList<>());
    }
    map.get(key).add(s);
}

โš ๏ธ Delimiter '#' is important โ€” avoids merging counts like "1#10" vs "11"

โœ… Time: O(n * k) โ€” faster than sorting

โ— Key becomes less readable (looks like a hash string), but grouping logic still works perfectly


๐Ÿงช Test Cases + Dry Run

โœ… Main Test Case 1

Input: ["eat", "tea", "ate"]

๐Ÿ” Dry Run

WordSorted KeyMap Update
eataet{aet: [eat]}
teaaet{aet: [eat, tea]}
ateaet{aet: [eat, tea, ate]}

โžก๏ธ Output: [["eat", "tea", "ate"]]


โœ… Main Test Case 2

Input: ["bat", "tab", "tan", "nat"]

๐Ÿ” Dry Run

WordSorted KeyMap Update
batabt{abt: [bat]}
tababt{abt: [bat, tab]}
tanant{abt: [bat, tab], ant: [tan]}
natant{abt: [bat, tab], ant: [tan, nat]}

โžก๏ธ Output: [["bat", "tab"], ["tan", "nat"]]


โš ๏ธ Edge Case 1 โ€“ Single Empty String

Input: [""]

๐Ÿ” Dry Run

WordSorted KeyMap Update
""""{"": [""]}

โžก๏ธ Output: [[""]]


โš ๏ธ Edge Case 2 โ€“ Multiple Empty Strings

Input: ["", ""]

๐Ÿ” Dry Run

WordSorted KeyMap Update
""""{"": [""]}
""""{"": ["", ""]}

โžก๏ธ Output: [["", ""]]


๐Ÿง  Key Insights & Gotchas

  • โœ… Initially thought of isAnagram() + compare with keys โ†’ O(nยฒ) โ†’ Not optimal
  • โœ… Sorting each word works well โ†’ O(k log k)
  • โšก Faster alt: use int[26] frequency count as key (less readable though)
  • โ— new String(char[]) is key โ†’ not arr.toString()
  • โœ… new ArrayList<>(map.values()) gives final output

โ›“๏ธ Similar Problems


๐Ÿ”š TL;DR

  • Group words by their sorted form
  • Map from sorted key โ†’ original words
  • Avoid extra comparisons โ€” sorting is the only grouping logic needed
0
Subscribe to my newsletter

Read articles from LilSardarX directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

LilSardarX
LilSardarX

Iโ€™m figuring things out in tech โ€” and blogging along the way. I write what I learn, so I can revise it later and maybe help you avoid a few rabbit holes.