Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Two strings are anagrams iff they have identical character counts. Fast-fail on differing lengths, then count characters of one string and decrement with the other — if every count returns to zero, it's an anagram. O(n) time, O(1) space for a fixed alphabet. Sorting both strings and comparing is the simpler O(n log n) fallback.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you model 'anagram' as a frequency-count equality and pick a fixed array over a hash map when the alphabet is small.
Where candidates slip
Skipping the length check, or assuming ASCII when the interviewer slips in 'what about Unicode?'.
How to stand out
Use a single counter (increment then decrement), state the O(1)-space claim depends on a bounded alphabet, and pivot cleanly to a hash map for Unicode.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewIs t an anagram of s?Lengths equal?NoYesExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Sort Both Strings
1Hint 1Explore hintHide hint
If two strings are anagrams, what happens when you sort each of them?
2Hint 2Explore hintHide hint
Sort the characters of both strings and compare the results for equality.
3Hint 3Explore hintHide hint
It's correct and tiny, but sorting costs O(n log n) — beatable with counting.
A · Core idea
Anagrams become identical once their letters are sorted into canonical order. So sort s, sort t, and check the two sorted forms are equal. A length mismatch makes them unequal automatically, but checking length first is a cheaper early exit.
Why it works. Sorting maps every permutation of the same multiset to the same string. Two strings share that canonical form exactly when they're anagrams. It's the easiest solution to get right under pressure.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "rat", t = "car"sort s
"rat" → "art"
s' = artfalseC · Code with teaching comments
Reference implementation previewpublic boolean isAnagram(String s, String t) { if (s.length() != t.length()) return false; char[] a = s.toCharArray(); char[] b = t.toCharArray(); Arrays.sort(a);Explore the full codeHide the full code
D · Why it works and complexityO(n log n) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
Sorting is the universal 'canonicalize then compare' trick — the same idea keys Group Anagrams. The downside is the log factor; counting removes it.
ComplexityO(n log n) time, O(n) space — Sorting dominates at O(n log n); the comparison is O(n). Space is O(n) for the sorted copies (Java char arrays / Python lists).
E · Edge cases and pitfalls2Skipping the length check and relying only on equality (still correct, just slightly slower to fail).Explore edge casesHide edge cases
Pitfalls
- Skipping the length check and relying only on equality (still correct, just slightly slower to fail).
- Forgetting sorting allocates O(n) extra space.
Approach 2 of 2 · Optimal
Frequency Count — One Counter (Optimal)
1Hint 1Explore hintHide hint
Anagrams have identical letter counts. How could you compare counts without sorting?
2Hint 2Explore hintHide hint
Build a frequency table from s, then subtract t's letters from it.
3Hint 3Explore hintHide hint
If any count goes negative (or isn't zero at the end), they aren't anagrams. With 26 letters the table is a fixed-size array → O(1) space.
A · Core idea
After the length check, walk s incrementing a count per character, then walk t decrementing the same counts. If t is an anagram, every increment is matched by a decrement and all counts end at zero. For a 26-letter alphabet, the counter is a fixed int[26] indexed by c - 'a' — constant space and great cache behavior.
Why one counter, not two. Building two maps and comparing them works but doubles the memory and adds a comparison pass. Incrementing then decrementing in a single table collapses the comparison into 'is everything zero?'. You can even bail early inside the second loop the moment a count dips below zero.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "aab", t = "aba"Hash-map state
count s
Continue scanning
build {a:2, b:1}
trueC · Code with teaching comments
D · Why it works and complexityO(n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
A frequency array is a histogram of the string. Anagram-ness is just histogram equality, and subtracting one histogram from another and checking for all-zero is the cleanest way to assert that. This exact histogram-compare powers Permutation in String and Find All Anagrams.
ComplexityO(n) time, O(1) space — Two linear passes over the strings and one over the 26-slot table = O(n). The table size is bounded by the alphabet, so space is O(1) for lowercase English.
E · Edge cases and pitfalls5s = "", t = "" — Equal length 0, no characters to count, all-zero table → true (empty strings are anagrams).Explore edge casesHide edge cases
Edge cases handled
s = "", t = ""Equal length 0, no characters to count, all-zero table → true (empty strings are anagrams).Unicode inputsSwitch the int[26] to a HashMap<Character,Integer> keyed by code point; the increment/decrement logic is unchanged.
Pitfalls
- Using int[26] for Unicode — index out of bounds or wrong results; use a map.
- Forgetting the length check, then the counter can falsely balance for prefixes.
- Comparing two separate maps instead of one signed counter — works but more code and memory.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic boolean isAnagram(String s, String t) { if (s.length() != t.length()) return false; int[] count = new int[26]; for (int i = 0; i < s.length(); i++) { count[s.charAt(i) - 'a']++;Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Hardcoding the 26-letter alphabet for Unicode
An int[26] indexed by c - 'a' breaks the moment a character falls outside a–z (negative index or overflow). For arbitrary characters, key a hash map by the character itself.
Explore the code comparisonHide the code comparison
Hardcoding the 26-letter alphabet for Unicode
An int[26] indexed by c - 'a' breaks the moment a character falls outside a–z (negative index or overflow). For arbitrary characters, key a hash map by the character itself.
Don't write this
int[] count = new int[26];
count[c - 'a']++; // crashes / wrong for non a-zWrite this instead
Map<Character, Integer> count = new HashMap<>();
count.merge(c, 1, Integer::sum);2Truthiness check that misses zero counts
In Python, checking `if count.get(c)` treats a legitimately-zero count as falsy. Always compare counts explicitly or verify all values equal zero.
Explore the code comparisonHide the code comparison
Truthiness check that misses zero counts
In Python, checking `if count.get(c)` treats a legitimately-zero count as falsy. Always compare counts explicitly or verify all values equal zero.
Don't write this
return not any(count.values()) # ok, but reads as 'no truthy', confusingWrite this instead
return all(v == 0 for v in count.values())Revise
Carry the pattern into the interview
Pattern to remember
Character frequency match via counting
Code pattern to memorize
if len(s) != len(t): return False
count = {}
for c in s: count[c] += 1
for c in t: count[c] -= 1
return all(v == 0 for v in count.values())Rules to remember
- 1Length check first — different lengths can never be anagrams, and it's a free O(1) exit.
- 2Increment for
s, decrement fortover one map — cleaner than building two maps and comparing. - 3A fixed 26-slot array beats a hash map for lowercase English —
O(1)space and faster constants. - 4For Unicode / arbitrary characters, use a hash map keyed by the code point, not a 26-array.
- 5Sorting both strings is the easy answer to fall back on, but say it's
O(n log n)vs theO(n)counter.
Use this pattern when
- Compare two strings for identical multiset of characters
- Detect rearrangements / permutations of a word
- Any 'same letters, different order' framing
Choose another pattern when
- Need to group many anagrams together → sort-key the words (Group Anagrams)
- Order matters, not just multiset → direct equality or sequence comparison
- Streaming / sliding comparison → fixed-window frequency (Permutation in String)
Interview takeaway
Anagram = equal character histograms. The counting array is the building block for Group Anagrams, Permutation in String, and Minimum Window Substring — all of which compare frequency maps.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternCharacter-frequency comparison. Reach for it whenever two strings must match as multisets of characters. The counting table is the seed of Group Anagrams, Permutation in String, and the sliding-window anagram problems.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Group AnagramsBucket words by their canonical (sorted or count-tuple) key.
- Permutation in StringSliding window comparing a fixed-size frequency table.
- Find All Anagrams in a StringComing soonSame window-frequency idea, collecting every matching start index.