Problem overview
The method isAnagram receives two strings, s and t, and returns true when t can be formed by rearranging all the letters of s, using each letter exactly as many times as it appears. Both strings are non-empty and use lowercase English letters.
For s = "anagram" and t = "nagaram" the answer is true: both strings contain three as, and one each of g, m, n and r. For s = "rat" and t = "car" the answer is false, because t has a c that s lacks.
This lesson's budget is O(n log n) time and O(n) extra space for sorted copies of the two strings. There are faster solutions, but they are not this lesson's subject.
Approach: sorting removes the order
Anagrams contain the same letters and differ only in their order. Sorting is a way of throwing the order away: once the letters of each string are in alphabetical order, two strings with the same letters turn into the very same string.
Here is the idea on the examples:
"anagram" sorted: aaagmnr
"nagaram" sorted: aaagmnr (equal, so anagrams)
"rat" sorted: art
"car" sorted: acr (different, so not anagrams)
Two strings are anagrams exactly when their sorted versions are equal, so the whole solution is "sort both, then compare". Strings of different lengths can never be equal after sorting, so that case needs no special handling.
Step by step
- Sort the letters of
sintosortedS. A TypeScript string has no sort method, because strings are immutable. The recipe has three parts:split("")makes an array of one-letter strings,sort()orders that array, andjoin("")glues the letters back into a string. - Do the same for
tto getsortedT. Both strings get identical treatment, so the results can be compared directly. - Return
sortedS === sortedT. Thejoin("")in the earlier steps is what makes this comparison valid: strings are compared by content.
The solution in TypeScript
class Solution {
isAnagram(s: string, t: string): boolean {
const sortedS = s.split("").sort().join("");
const sortedT = t.split("").sort().join("");
return sortedS === sortedT;
}
}
The TypeScript details worth a closer look:
- Why
splitandjoin.sort()is an array method."anagram".split("")gives["a", "n", "a", "g", "r", "a", "m"], andjoin("")reverses the conversion after sorting. sort()with no comparator. By default it sorts by converting elements to strings and comparing their UTF-16 code units. For single lowercase letters that is exactly alphabetical order. It would be wrong for numbers:[10, 9, 1].sort()gives[1, 10, 9]. For this lesson no comparator is needed.sort()changes the array in place. It also returns the same array. Here that is harmless, because the array fromsplitis a temporary copy, andsitself is never modified.- Comparing arrays with
===doesn't work. On arrays it asks whether they are the same object, not whether their contents match, so["a", "b"] === ["a", "b"]isfalse. For two array literals tsc reports "This condition will always return 'false' since JavaScript compares objects by reference, not value." Joining the letters into strings first is what lets===compare contents.
Complexity
Let n be the length of s and m the length of t.
Time: O(n log n + m log m). Splitting and joining are linear. Sorting costs O(n log n) for s and O(m log m) for t, and comparing the results is linear. When the lengths are equal, which they must be for an anagram, this is O(n log n).
Space: O(n + m). The arrays of letters and the sorted strings are copies of the input.
Edge cases and common mistakes
- Different lengths.
"ab"and"a"givefalseautomatically, since the sorted strings have different lengths. - Same letters, different counts.
"aab"and"abb"have the same set of letters but not the same counts. Their sorted strings,aabandabb, differ, so the answer isfalse. A check that only asks whether each letter oftappears inswould wrongly accept them. - Identical strings. A word is an anagram of itself, so
"abc"and"abc"givetrue. - Skipping
join. Comparing the two sorted arrays with===returnsfalseeven for real anagrams, because they are different array objects. - Calling
sorton a string.s.sort()doesn't exist, becausesortbelongs to arrays. The compiler reports it, and at run time it would throw aTypeError.
Testing
s = "anagram",t = "nagaram"givestrue.s = "rat",t = "car"givesfalse.s = "ab",t = "a"givesfalse.s = "aab",t = "abb"givesfalse.
