DSA Patterns Made Simple · Arrays and Strings: The Six Core Patterns

Counting and Grouping

Count how often things appear, or group items that share a key. Anagrams, most-frequent elements and 'first unique character' all fall to a frequency map.

Create a free account to save this page, take notes and track your progress. Sign in

The problem

Valid Anagram: are "listen" and "silent" made of exactly the same letters? A person would tally the letters of each word and compare the tallies. That's the whole pattern.

How to spot it

  • "Same letters / same items, any order" (anagrams, permutations of a string)
  • "Most frequent", "top k", "appears more than n/2 times"
  • "First unique", "count of each"
  • "Group together items that are alike" (group anagrams, group by property)

The idea

Build a frequency map: item → how many times it appears. Comparing two maps answers "same items?", looking for the largest counts answers "most frequent?", and counts of 1 answer "unique?".

For grouping, the map goes from a key that alike items share to a list of those items. For anagrams, the key is the sorted letters: "eat", "tea" and "ate" all become "aet".

The template

from collections import Counter, defaultdict

counts = Counter(items)                  # item -> count

groups = defaultdict(list)               # key -> items sharing that key
for item in items:
    groups[key_of(item)].append(item)

Worked example

Three problems, one idea: valid anagram (compare counts), first unique character (find count 1), and group anagrams (group by sorted letters).

Anagrams, first unique character and groupingTry it yourself
from collections import Counter, defaultdict


def is_anagram(a, b):
    return Counter(a) == Counter(b)


def first_unique_char(s):
    counts = Counter(s)
    for i, ch in enumerate(s):
        if counts[ch] == 1:
            return i
    return -1


def group_anagrams(words):
    groups = defaultdict(list)
    for w in words:
        groups["".join(sorted(w))].append(w)   # "tea" -> "aet"
    return list(groups.values())


print("listen/silent:", is_anagram("listen", "silent"))
print("rat/car:", is_anagram("rat", "car"))
print("first unique in 'leetcode':", first_unique_char("leetcode"))
print("first unique in 'aabb':", first_unique_char("aabb"))
for group in group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"]):
    print(" ".join(group))

Output

listen/silent: True
rat/car: False
first unique in 'leetcode': 0
first unique in 'aabb': -1
eat tea ate
tan nat
bat

Common mistakes

  • Sorting both strings to check anagrams works (O(n log n)) but counting is O(n). Mention both; either is usually accepted.
  • Comparing JavaScript Maps with ===. Two maps are never === equal; compare entry by entry (or check lengths first, as above).
  • Picking a weak group key. For anagrams, the key must be the same for all anagrams and different otherwise; sorted letters or a 26-letter count both work.
  • Top-k by sorting the whole map is O(n log n); a heap or bucket sort can do better (see the Heaps lesson).

Practice

Key points

  • Frequency map: item → count. Compare maps, find count 1, or find the largest counts.
  • Grouping: key → list of items that share the key.
  • Choose a key that is identical for "alike" items (sorted letters for anagrams).
  • Counting is O(n) time and O(k) space for k distinct items.

Practice questions

How would you check whether two strings are anagrams in O(n)?

Answer

Count the characters of each (a frequency map or a 26-slot array for lowercase letters) and compare the counts. Different lengths can be rejected immediately.

What key would you use to group anagrams together?

Answer

Any value that is the same for all anagrams of a word: the word's letters sorted ("tea" → "aet"), or a tuple of 26 letter counts, which avoids sorting.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?