skip to content

How do sorting and character counting compare as canonical forms for anagram detection?

level: middleimportance: must knowfreq 74%

answer

  1. Both reduce two inputs to one shape
  2. Which operation dominates each cost?
  3. Comparison sorting cannot beat L log L
  4. Tallying touches each character exactly once
  5. What alphabet did the fixed table assume?

basics

~20 s

Sorting both inputs and comparing produces a canonical form in O(L log L); tallying character frequencies produces one in O(L). Counting wins asymptotically, but only if the tally covers the real alphabet rather than a fixed table of 26 lowercase letters.

solid answer

~50 s

Both are the same idea — reduce each input to a canonical form and test the forms for equality — and both answer the question "do these hold the same multiset of characters?". Sorting costs O(L log L) because comparison sorting cannot beat that bound, plus O(L) space if you must not mutate the input. Counting visits each character once, so it is O(L) time and O(k) space for an alphabet of size k, and it is the better choice on long inputs. The trap is the fixed 26-slot tally: it silently assumes every character is a lowercase letter of one script, so digits, punctuation, mixed case and accented letters either corrupt the tally or crash the indexing. A tally keyed by character generalises at the cost of a constant factor. Whichever you pick, compare lengths first — different lengths can never be anagrams, and that check is O(1).

go deeper

for a junior

Be ready to say what anagram equivalence means — same characters, same multiplicities, order ignored — and to name both ways of building a canonical form with their costs.

for a middle

Explain why the sorting route is bounded by O(L log L) while counting is O(L), and what the fixed-size tally quietly assumes about the input alphabet.

for a senior

Show judgment on the real inputs: pick the tally keyed by character when the alphabet is open, add the length precondition, and say which unit of text you are counting rather than assuming one.

for a principal

Own the tradeoff at scale: if the feature groups millions of entries rather than comparing pairs, the canonical form becomes a stored key, and its size, stability and alphabet assumptions turn into a data-format commitment.

## The shared idea: a canonical form Two sequences are anagrams when they contain the same characters with the same multiplicities — the same **multiset**, ignoring order. Direct multiset comparison is awkward, so both standard techniques do the same trick: map each input to a *canonical form*, a representative that is identical for all members of an equivalence class, then test the two forms for equality. Sorting and counting differ only in which canonical form they build. The worked setting here is a word game that must catch shuffled duplicate answers: a player submits a word, and the server must decide whether it is a rearrangement of one already accepted this round. ## Canonical form by sorting Sort the characters of each input and compare the sorted sequences. The forms are equal exactly when the multisets are equal, because sorting is a deterministic function of the multiset alone. - **Time:** O(L log L) for length L, dominated by the sort. This is not an implementation weakness — any sort that works by comparing elements is bounded below by Omega(L log L) comparisons in the worst case. Escaping it means not comparing at all, which is precisely what counting does. - **Space:** O(L) if you must preserve the original input, since you sort a copy; O(1) or O(log L) auxiliary if you are allowed to sort in place, depending on the sort. - **Advantages:** the form is a directly comparable, orderable value, which makes it a natural grouping key when you want to bucket many inputs by anagram class rather than compare two. It also makes no assumption whatsoever about the alphabet. ## Canonical form by counting Walk each input once, incrementing a tally per character, then compare the tallies. - **Time:** O(L) to build each tally, plus O(k) to compare them for an alphabet of size k. A common refinement builds the tally from the first input, decrements while scanning the second, and aborts the moment a count goes negative; combined with an up-front length check this makes the whole comparison O(L) with no O(k) sweep at the end. - **Space:** O(k) — the tally, not the input. For a small fixed alphabet that is a constant. - **The classic wrong answer:** "a 26-slot count array is *the* canonical form." It is *a* canonical form for exactly one input class: lowercase letters of a single 26-letter script. The slot index is usually derived by subtracting the code of the first letter, so a digit, a space, an uppercase letter or an accented letter maps to a negative or out-of-range slot — an out-of-bounds access if you are lucky, a silently corrupted tally if the slot happens to land inside the table. The general form is a tally keyed by character, which costs a constant factor more per character and works for any alphabet. This is where mainstream runtimes visibly disagree on what "a character" even is — some expose text as fixed-width code units, others as variable-width sequences, and a tally that assumes one model produces different answers under the other. The fix at this tier is to state which unit you are counting rather than to assume. ## Choosing between them | | Sorting | Counting | |---|---|---| | Time | O(L log L) | O(L) | | Extra space | O(L) copy, or in-place | O(k) tally | | Alphabet assumption | none | must match the real input set | | Good as a grouping key | directly | only if serialised in a fixed order | On long inputs counting wins clearly. On very short inputs the log factor is a small multiplier and constants dominate, so sorting can measure faster while being simpler to reason about — asymptotic superiority promises nothing at small L. If you need to group thousands of inputs into anagram classes, the sorted form is the more convenient key, because a tally must be serialised into a fixed, deterministic order before it can be used as one. ## The cheap check people forget If the two inputs have different lengths they cannot be anagrams. That is O(1) and rejects a large share of pairs before any O(L) work. It is also a correctness guard for the decrement-and-abort variant: without an equal-length precondition, "no count went negative" does not prove the multisets match — the second input could simply be shorter and use up only part of the tally. ## What interviewers listen for The complexity of each form stated correctly and attributed to the right cause; the alphabet assumption named without being prompted; the length check offered as a first move; and an honest "it depends on L and on whether I am comparing a pair or grouping many" rather than a reflexive "counting is always better".

  • When would sorting actually be the better choice?
    On short inputs, where the log factor is a small multiplier and constants decide; when the alphabet is unknown or wide, so a tally would be awkward or large; and when you are grouping many inputs into anagram classes rather than comparing a pair, because a sorted form is directly usable as a key while a tally must first be serialised in a fixed order.
  • Why compare lengths before doing anything else?
    Different lengths make anagram status impossible, so an O(1) test rejects many pairs before any O(L) or O(L log L) work. It is also a correctness precondition for the decrement-and-abort tally variant: without equal lengths, "no count went negative" does not prove the multisets match, since a shorter second input can consume only part of the tally.
  • How do you compare two tallies without sweeping the whole alphabet?
    Build the tally from the first input, then decrement while scanning the second and abort the instant a count goes negative. With lengths checked equal up front, surviving the scan proves the multisets are identical. That keeps the comparison proportional to L rather than to alphabet size k, which matters when k is large and L is small.

Two shopping receipts list the same purchase in different order. Sorting is rewriting both lists alphabetically and reading them side by side; counting is tallying how many of each item appears and comparing the tallies.

saying these in an interview costs you the question

  • Says a 26-slot count array is always the canonical form
  • Claims sorting-based comparison runs in O(L) time
  • Forgets that different lengths rule out anagram status
  • Assumes counting works unchanged for mixed case or accents
  • Calls counting better without ever mentioning alphabet size

context