CMD Guide
HomeDSAAdvanced Patterns

Introduction to Counting Pattern

Counting works by trading a second linear pass (or nested pairwise comparisons) for a single pass plus O(1)-amortized lookups into a hash map or fixed-size array, so you can answer "how many", "which one is most/least frequent", or "does any value exceed a threshold" without re-scanning the collection for every element.

Recognize the pattern

Brute force → optimal

Brute force: for each character, scan the remaining string and count matches. Left as-is, this is O(n²) time, O(1) space — but it will re-report the same tally once per occurrence (e.g. it prints "s appears 4 times" four separate times for "mississippi", once per each 's'). To report each distinct character exactly once, you additionally track which characters have already been tallied in a seen-set, which costs O(k) extra space (k = distinct characters) on top of the same O(n²) time. So the O(1)-space brute force and the O(k)-space brute force are the same nested-loop core with or without duplicate-suppression bookkeeping — neither changes the O(n²) time bound.

Optimal (counting pattern): a single pass where each character looks up and increments its own slot in a hash map (or a 26-length int array for lowercase letters). Both operations are O(1) amortized, so the whole scan is O(n).

Complexity, derived

Time: the loop body runs exactly n times (once per input character). Each iteration performs one map get and one put/array increment, each O(1) amortized — array indexing is O(1) worst-case, which is exactly why bounded-alphabet problems prefer arrays over hash maps. Total work = n × O(1) = O(n).

Space: the map stores at most one entry per distinct value. If k is the number of distinct characters, space is O(k); in the worst case (all characters unique) k = n, so space is O(n). With a fixed alphabet of size 26, space collapses to O(1) regardless of n.

Traced example: count("mississippi")

StepCharActionMap so far
1mnot present → add{m:1}
2inot present → add{m:1, i:1}
3snot present → add{m:1, i:1, s:1}
4spresent → increment{m:1, i:1, s:2}
5ipresent → increment{m:1, i:2, s:2}
6spresent → increment{m:1, i:2, s:3}
7spresent → increment{m:1, i:2, s:4}
8ipresent → increment{m:1, i:3, s:4}
9pnot present → add{m:1, i:3, s:4, p:1}
10ppresent → increment{m:1, i:3, s:4, p:2}
11ipresent → increment{m:1, i:4, s:4, p:2}

Final: {m:1, i:4, s:4, p:2} after exactly 11 O(1) steps.

Java: character frequency count

import java.util.LinkedHashMap;
import java.util.Map;

public class CharFrequency {
    public static Map<Character, Integer> countChars(String s) {
        Map<Character, Integer> freqMap = new LinkedHashMap<>();
        for (char c : s.toCharArray()) {
            freqMap.merge(c, 1, Integer::sum); // O(1) amortized get+put
        }
        return freqMap;
    }

    public static void main(String[] args) {
        System.out.println(countChars("mississippi"));
        // {m=1, i=4, s=4, p=2}
    }
}

Go: character frequency count

package main

import "fmt"

func countChars(s string) map[rune]int {
	freq := make(map[rune]int)
	for _, c := range s {
		freq[c]++ // O(1) amortized read-modify-write
	}
	return freq
}

func main() {
	fmt.Println(countChars("mississippi"))
	// map[i:4 m:1 p:2 s:4]
}

Pitfalls

When to use / when not — trade-offs

Use counting when you need frequencies, duplicates, majority elements, or condition-based tallies over an unordered collection. It beats the nested-loop brute force (O(n²) time, O(1) space, or O(n²) time and O(k) space if you add a seen-set to suppress duplicate reports) by trading extra space for guaranteed O(n) time — almost always a win unless memory is the binding constraint and k is close to n.

Compare to sorting-then-scanning (e.g. to find duplicates or the majority element): sorting costs O(n log n) time. Space depends on the sort: an iterative in-place sort like heapsort is O(1) extra space, but the common in-place choices (quicksort, introsort) recurse and typically cost O(log n) stack space, not true O(1). Counting instead costs O(n) time but O(k) space. Choose sorting when memory is scarce and the log n time overhead (and log n stack space, if using quicksort/introsort) is acceptable; choose counting when you need true linear time and can afford the map/array.

Do not reach for counting when element order or adjacency matters (use sliding window / two pointers instead), or when the value range is enormous and sparse with few repeats (a hash map's overhead may not pay off vs. a direct comparison approach).

Takeaways

Recall question: Why does switching from a HashMap<Character,Integer> to a fixed int[26] array for lowercase-only input improve worst-case time complexity, not just constant factors?


Synthesized from the original counting-pattern walkthrough plus standard interview-prep treatments of hashing-based counting (frequency maps, majority element, anagram detection) and complexity analysis by first principles.

🤖 Don't fully get this? Learn it with Claude

Stuck on Introduction to Counting Pattern? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Introduction to Counting Pattern** (DSA) and want to truly understand it. Explain Introduction to Counting Pattern from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Introduction to Counting Pattern** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Introduction to Counting Pattern** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Introduction to Counting Pattern** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes