Problem overview
The method isAnagram(s: String, t: String): Boolean returns true when t can be made by rearranging the letters of s, using every letter exactly once, and false otherwise. Both strings are lowercase and have between 1 and 50,000 characters.
With s = "anagram" and t = "nagaram" the answer is true: both strings have three a, and one each of g, m, n and r. With s = "rat" and t = "car" the answer is false, because t has a c that s lacks.
The lesson asks for O(n log n) time and O(n) extra space. Faster ways to do this exist, and they come in later lessons. This one uses sorting.
Approach: sorting removes the order
Two strings are anagrams exactly when they hold the same letters the same number of times. The only thing that differs is the order, and sorting takes the order away. Sort the letters of "anagram" and you get "aaagmnr". Sort the letters of "nagaram" and you get "aaagmnr" too. Equal sorted strings mean equal letter counts, and the other way round.
For "rat" and "car" the sorted strings are "art" and "acr". They differ at the second character, so the strings are not anagrams.
The plan is therefore three lines: sort s, sort t, compare the results.
Different lengths need no special case. A string of length 3 can never equal a string of length 4, so if s = "ab" and t = "abb", the comparison is false by itself.
Step by step
- Sort
s.val sortedS = s.sortedkeeps the letters ofsin alphabetical order. On aString,sortedreturns a newString, sosortedSis a string, ready to compare. - Sort
tthe same way. Both words go through the same treatment, which is what makes the comparison fair. - Compare.
sortedS == sortedTistrueif and only if the two sorted strings have the same characters in the same places. The expression is the method's result.
The solution in Scala
class Solution {
def isAnagram(s: String, t: String): Boolean = {
val sortedS = s.sorted
val sortedT = t.sorted
return sortedS == sortedT
}
}
The Scala details worth a closer look:
sortedreturns a new value. Strings are immutable in Scala, sos.sortedleavessunchanged and gives back a new string. Afterval sortedS = s.sorted, bothsandsortedSare usable. Those two copies are theO(n)extra space.- Sorting a string gives a string. A
Stringis treated as a sequence ofCharvalues. Callingsortedon it orders them by character code and builds aStringagain. For lowercase letters, character code order is alphabetical order. ==compares contents. In Scala,==on strings compares their characters, not their identity, so two different string objects with the same letters are equal. Unlike Java, there is no need forequals.valversusvar. The sorted strings are never reassigned, sovalis the right choice.- The
returnkeyword. Scala methods return the value of their last expression, sosortedS == sortedTalone would do. The explicitreturnbehaves the same here, because it is not inside a loop or a function literal.
Complexity
Time: O(n log n + m log m), where n and m are the lengths of s and t. Each sort takes time proportional to its length times the logarithm of it, and the final comparison walks the strings once, which is O(n) and doesn't change the total. If the lengths differ, the comparison stops at once. With n the larger of the two lengths, this is O(n log n).
Space: O(n + m). The two sorted copies are new strings with as many characters as the originals.
Counting how many times each letter appears, in an array of 26 integers, avoids sorting and gives O(n) time. This lesson keeps to sorting because it needs the fewest lines and the fewest places for a mistake.
Edge cases and common mistakes
- Different lengths.
"ab"and"abb"are not anagrams. Sorting gives"ab"and"abb", which are unequal, so the comparison returnsfalsewith no extra check. - Same letters, different counts.
"aab"and"abb"have the same length and the same set of letters, yet they are not anagrams. Sorting gives"aab"and"abb", which differ. Checking only that each letter oftappears somewhere inswould wrongly accept this pair. - Identical strings. A string is an anagram of itself, so
"abc"and"abc"givetrue. - A single letter.
"a"and"b"givefalse, and"a"and"a"givetrue. - Expecting
sto be sorted afterwards.s.sorteddoesn't changes. The sorted version is only in the new value, so you have to use that value.
Testing
s = "anagram",t = "nagaram"returnstrue.s = "rat",t = "car"returnsfalse.s = "ab",t = "abb"returnsfalse.s = "aab",t = "abb"returnsfalse.
