Post

Arrays: Group Anagrams — Kotlin Solution

Arrays: Group Anagrams — Kotlin Solution

Problem Info

  
LeetCode #49 — Group Anagrams
DifficultyMedium
TopicArrays, HashMap, Sorting

Given an array of strings strs, group the anagrams together. You can return the answer in any order.

An anagram uses all the original characters exactly once — just rearranged.

Example:

1
2
3
4
5
6
7
8
Input:  strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Input:  strs = [""]
Output: [[""]]

Input:  strs = ["a"]
Output: [["a"]]

Constraints:

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

Approach

Two strings are anagrams if and only if they have the same characters with the same frequency.

That means if you sort the characters of two anagrams, you get the same string.

Key insight: Use the sorted form as a HashMap key — all anagrams map to the same key, so they naturally land in the same bucket.

Walk through ["eat","tea","tan","ate","nat","bat"]:

1
2
3
4
5
6
7
8
"eat" → sorted → "aet"  → map["aet"] = ["eat"]
"tea" → sorted → "aet"  → map["aet"] = ["eat","tea"]
"tan" → sorted → "ant"  → map["ant"] = ["tan"]
"ate" → sorted → "aet"  → map["aet"] = ["eat","tea","ate"]
"nat" → sorted → "ant"  → map["ant"] = ["tan","nat"]
"bat" → sorted → "abt"  → map["abt"] = ["bat"]

Result: [["eat","tea","ate"],["tan","nat"],["bat"]] ✓

This is a natural extension of Valid Anagram (LC 242) — instead of comparing two strings, we’re grouping many under a shared canonical form.


Kotlin Solution

Approach 1 — Sort as key (idiomatic Kotlin)

1
2
3
4
5
6
fun groupAnagrams(strs: Array<String>): List<List<String>> {
    return strs
        .groupBy { it.toCharArray().sorted().joinToString("") }
        .values
        .toList()
}

Kotlin’s groupBy does exactly what we need in one expression — no manual map management.

Approach 2 — Frequency count as key (optimal)

1
2
3
4
5
6
7
8
9
10
11
12
13
fun groupAnagrams(strs: Array<String>): List<List<String>> {
    val map = HashMap<String, MutableList<String>>()

    for (s in strs) {
        val count = IntArray(26)
        for (c in s) count[c - 'a']++

        val key = count.joinToString(",")
        map.getOrPut(key) { mutableListOf() }.add(s)
    }

    return map.values.toList()
}

Approach 3 — Explicit HashMap with sorted key

1
2
3
4
5
6
7
8
9
10
fun groupAnagrams(strs: Array<String>): List<List<String>> {
    val map = HashMap<String, MutableList<String>>()

    for (s in strs) {
        val key = s.toCharArray().also { it.sort() }.joinToString("")
        map.getOrPut(key) { mutableListOf() }.add(s)
    }

    return map.values.toList()
}

Why the Frequency Count Key Is Faster

Sorting each string costs O(k log k). Building a frequency count costs O(k) — a meaningful difference when strings can be up to 100 chars long.

 Sort KeyFrequency Count Key
Key build costO(k log k) per stringO(k) per string
Total timeO(n · k log k)O(n · k)
SpaceO(n · k)O(n · k)
Readability✅ Clear⚠️ Slightly more verbose

count.joinToString(",") — turning the frequency array into a deterministic string key:

1
2
3
4
5
val count = IntArray(26)
for (c in s) count[c - 'a']++
val key = count.joinToString(",")
// "eat" → "1,0,0,0,1,0,...,1,0,0,0"  (a=1, e=1, t=1)
// "tea" → same key ✓

map.getOrPut(key) { mutableListOf() }.add(s) — clean Kotlin idiom:

1
2
3
map.getOrPut(key) { mutableListOf() }.add(s)
// vs Java: if (!map.containsKey(key)) map.put(key, new ArrayList<>());
//          map.get(key).add(s);

When to Use Which Approach

ApproachUse When
groupBy (Approach 1)Clean production code, readability first
Frequency count (Approach 2)Performance-critical, long strings
Explicit HashMap (Approach 3)Interview — shows deliberate key construction

Follow-up — What if strings can contain Unicode?

The IntArray(26) key only works for lowercase a-z. For Unicode strings, fall back to the sorted string key — it handles any character set without changes.


Complexity

 Sort KeyFrequency Count Key
TimeO(n · k log k)O(n · k)
SpaceO(n · k)O(n · k)

Where n = number of strings, k = max string length.


Key Takeaway

Anagrams share a canonical form. Sort the characters or count their frequencies to get a key every anagram in a group maps to identically. One HashMap pass groups them all — no comparisons between strings needed.

🔗 Solve it on LeetCode →


📚 Kotlin DSA Series

This post is part of the Kotlin DSA series — solving LeetCode problems using idiomatic Kotlin.

← View Full Series Index
This post is licensed under CC BY 4.0 by the author.