Are two strings anagrams?

Sorting both and comparing is O(n log n) and fits on one line. Counting is O(n): add up the letters of the first, subtract the letters of the second, and if every count reaches zero they are anagrams. Check lengths first — it is a free rejection.

Overview

The problem

Two strings are anagrams if one is a rearrangement of the other — same characters, same counts, different order.

InputAnagram?
"listen", "silent"Yes
"anagram", "nagaram"Yes
"rat", "car"No
"a", "ab"No — different lengths
"", ""Yes

The first thing to establish, and a legitimate clarifying question: is it case sensitive, and do spaces and punctuation count? "Dormitory" and "dirty room" are anagrams under one interpretation and not under another.

StringsCoding problemEasy

Step through it

What to watch

  • The counts rise on the first string and fall on the second.
  • Reaching zero and being deleted is what makes the final check a simple emptiness test.
  • A length mismatch rejects before any counting starts.

Say this out loud

"Sorted comparison is the one-liner, O(n log n). Better is a Counter: add one string, subtract the other, and everything should cancel. O(n) time, O(k) space in the alphabet size."

Are two strings anagrams?

Check whether two strings are anagrams of each other.

Run it

Three implementations timed against each other on real words, so the O(n) against O(n log n) claim is measured rather than asserted — and a Unicode case that breaks the fixed-array version.

1Python
Output
2Python
Output
3Python
Output

Two solutions, and the trade between them

Sorting:

4Python
Output

One line, O(n log n) time, O(n) space. Correct, and it does more work than the question requires — the order of the sorted characters is irrelevant to the answer.

Counting:

5Python
Output

O(n) time, O(k) space where k is the alphabet size. The length check is a cheap early exit and is not strictly necessary, since unequal lengths give unequal counters.

 SortingCounting
TimeO(n log n)O(n)
SpaceO(n)O(k) — bounded by the alphabet
One-linerYesNearly
Extends to grouping anagramsYes — sorted string as a keyYes — count tuple as a key

Mention both. Sorting shows you can produce a correct answer immediately; counting shows you noticed the sort was unnecessary work.

The manual counting version

If asked to avoid library helpers:

6Python
Output

One dictionary, two passes, and the second pass decrements rather than building a second count — which is the version worth knowing, because it uses half the space of comparing two dictionaries.

For a known small alphabet, an array of 26 integers replaces the dictionary and is faster:

7Python
Output

Single pass over both strings simultaneously, incrementing for one and decrementing for the other. Worth offering as the optimised version when the input is guaranteed lowercase ASCII — and worth noting that it breaks on Unicode, which is exactly the follow-up.

Sorting versus counting

sorted(a) == sorted(b) is correct, obvious and O(n log n). It is a perfectly good answer to give first, and then improve.

Counting is O(n). Build a frequency map of the first string, walk the second decrementing, and check nothing is left over. With a Counter that is two lines; done by hand it is a loop and a dictionary.

Both need the length check first. Different lengths cannot be anagrams, and rejecting there avoids the work entirely.

Space, and the alphabet

The counter holds one entry per distinct character, so space is O(k) in the alphabet rather than O(n) in the input. For lowercase ASCII that is at most 26 entries, which is why interviewers sometimes ask for a fixed 26-element array instead — the same algorithm with the dictionary replaced by an offset from ord('a').

Say out loud that this assumes an alphabet. For arbitrary Unicode the fixed array is wrong and the dictionary is the right structure.

The follow-up that catches people

"Group all the anagrams in a list of words." The instinct is to compare every pair, which is O(n²) comparisons of O(m) strings. The answer is to give every word a canonical key — its sorted letters, or its count tuple — and group by that key in one pass. That is the next question on this track.

Unicode, the interesting complication

The array version assumes 26 lowercase letters. Real text does not.

Accented characters. "café" and "éfac" are anagrams, and ord('é') - 97 is out of range for a 26-element array.

Composed and decomposed forms. "é" can be one code point or an "e" plus a combining accent. Those two strings look identical, contain different characters, and would be reported as non-anagrams. unicodedata.normalize("NFC", s) resolves it, and it should be the first step for any text from the outside world.

Case folding. If case is to be ignored, casefold() is more correct than lower() — it handles cases such as the German ß expanding to "ss".

8Python
Output

Raising the normalisation point unprompted is a strong signal in an interview, because it shows awareness that "same characters" is not as simple as it appears.

Edge cases

CaseAnswer
Both emptyYes
Different lengthsNo — check first, it is O(1)
Same stringYes — a string is an anagram of itself
Spaces and punctuationDepends on the definition; ask
Case differencesDepends; ask
Very long stringsCounting is O(n) and preferable
Non-ASCIINormalise first, and use a dictionary rather than a fixed array

The follow-ups

"Group all anagrams together." The standard escalation. Use the sorted string, or a tuple of counts, as a dictionary key:

9Python
Output

O(n × m log m) for n words of length m. A count tuple as the key gives O(n × m) at the cost of a fixed-size key.

"Find all anagrams of a pattern inside a longer string." A sliding window of the pattern's length with a running character count, updated incrementally as the window moves — O(n) rather than re-counting each window.

"What if you cannot use extra space?" Sorting in place, if the strings are mutable. In Python they are not, so this is a question about other languages.

"How would you check a million pairs?" Precompute a canonical form — the sorted string or a count signature — once per string, and compare those. That turns each comparison into a hash lookup.

What it is testing

Do you notice that sorting is more work than needed? Both answers are correct, and recognising the O(n) alternative is the point.

Do you ask about the definition? Case, whitespace and punctuation all change the answer, and asking is expected rather than pedantic.

Do you check lengths first? A one-line early exit that is free.

Do you see the grouping follow-up coming? Using a canonical form as a dictionary key is the transferable idea, and it appears in deduplication, caching and indexing far beyond this problem.

Recap in one screen

  • Anagram means identical character counts, so compare counts — O(n) — rather than sorting at O(n log n).
  • Counter(s) == Counter(t) is the practical answer; a 26-element array is the optimised ASCII version.
  • Ask about case, whitespace and punctuation before coding.
  • Normalise Unicode before comparing, or visually identical strings compare as different.
  • The canonical-form-as-dictionary-key idea is what generalises, and grouping anagrams is the standard follow-up.

How the code works

Three implementations timed against each other on real words, so the O(n) against O(n log n) claim is measured rather than asserted — and a Unicode case that breaks the fixed-array version.

How the code works

  1. if len(a) != len(b): return FalseFree, and it removes a whole class of input before any counting. Interviewers notice when it is missing.
  2. del counts[ch]Deleting on zero is what lets the final test be not counts. Leaving zeros in means comparing against a dictionary of zeros instead, which works and reads worse.
  3. if ch not in counts: return FalseCatches a character that is in b and not in a without letting the count go negative. Skipping it gives wrong answers on inputs of equal length.
  4. seen = [0] * 26The fixed-array version, and its assumption. Fast and small for lowercase ASCII, and an IndexError the moment an accent arrives — which the last line demonstrates.

Change one thing

  • Compare Counter(a) == Counter(b) in the timing loop. It is the C implementation of the same idea and wins comfortably.
  • Make the array version case-insensitive with a.lower(). It fixes one assumption and leaves the alphabet one in place.

Where this runs

Real CPython, compiled to WebAssembly and running on your own machine — nothing is uploaded. The first run takes a few seconds while the interpreter downloads; after that it is immediate. Need more room, or want to paste your own attempt? Use the Python compiler.

Check yourself

0 of 3

Answer without scrolling back up.

  1. Counting beats sorting for anagram checks because it is:

  2. Why delete a key when its count reaches zero?

  3. A fixed 26-element array version breaks on:

Cheat sheet

Are two strings anagrams?

Sorting both and comparing is O(n log n) and fits on one line. Counting is O(n): add up the letters of the first, subtract the letters of the second, and if every count reaches zero they are anagrams. Check lengths first — it is a free rejection.

INTERVIEW · vizlearn.in/interview/valid-anagram.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.