Problem overview
The method isAnagram(String s, String t) returns true when t can be formed by rearranging the letters of s, using every letter exactly once, and false otherwise. Both strings hold lowercase English letters and have between 1 and 50,000 characters.
For s = "anagram" and t = "nagaram" the answer is true: both words have three a's and one each of g, m, n and r. For s = "rat" and t = "car" the answer is false, since t has a c where s has a t.
The lesson asks for O(n log n) time, with O(n) extra space for the sorted copies. Faster counting-based solutions exist, but this lesson uses sorting.
Approach: sorting removes the order
Two words are anagrams exactly when they have the same letters with the same counts. Only the order differs, and order is the one thing sorting throws away. After sorting, an anagram pair becomes two identical rows of letters, and a pair that isn't an anagram stays different:
s = "anagram" sorted: aaagmnr
t = "nagaram" sorted: aaagmnr -> equal, so anagrams
s = "rat" sorted: art
t = "car" sorted: acr -> different, so not anagrams
So the whole solution is to sort both strings and compare. Strings of different lengths can never be anagrams, and the final comparison handles that without extra code.
Step by step
- Copy
sinto achar[]and sort it. A JavaStringis immutable and can't be sorted, sotoCharArray()builds a new array holding its letters.Arrays.sortthen orders that array in place. The original string stays as it was. - Do the same for
t, into its own array, so the two rows can be compared position by position. - Compare the arrays with
Arrays.equals. It checks the lengths first and then each letter in turn, and returns the answer directly.
The solution in Java
class Solution {
public boolean isAnagram(String s, String t) {
char[] sortedS = s.toCharArray();
Arrays.sort(sortedS);
char[] sortedT = t.toCharArray();
Arrays.sort(sortedT);
return Arrays.equals(sortedS, sortedT);
}
}
The code uses java.util.Arrays, so a standalone file needs import java.util.Arrays; (or java.util.*) at the top.
The Java details worth a look:
- Arrays are compared with
Arrays.equals. Using==on two arrays asks whether they are the very same array in memory, and so does theequalsmethod that arrays inherit. For two separate arrays holdingaandb, bothsortedS == sortedTandsortedS.equals(sortedT)arefalseeven when the contents match.Arrays.equals(sortedS, sortedT)compares the contents. - Sorting works on the array, not the string. There is no
Arrays.sort(s)for aString: it doesn't compile. The copy intochar[]is what makes sorting possible. - Characters sort by their code.
Arrays.sorton achar[]orders letters by character code, which for lowercase English letters is alphabetical order. - An alternative ending.
new String(sortedS).equals(new String(sortedT))gives the same answer, but builds two more objects. Comparing the arrays directly is simpler.
Complexity
Time: O(n log n + m log m), where n and m are the lengths of s and t. Sorting each array costs that much, and the final comparison is linear. When the lengths are equal, which is the only case that can return true, this is O(n log n).
Space: O(n + m). The two sorted arrays are copies of the input strings. Sorting itself may use some extra working memory on top of that, which doesn't change the total.
Edge cases and common mistakes
- Different lengths.
isAnagram("ab", "abc")isfalse:Arrays.equalsreturnsfalsefor arrays of different lengths. - Same letters, different counts.
"aab"and"abb"have the same set of letters but are not anagrams. Sorting givesaabandabb, which differ. Comparing only which letters appear would get this wrong. - Identical strings. A word is an anagram of itself, so
isAnagram("abc", "abc")istrue. - Forgetting to sort one of the strings. Comparing a sorted array with an unsorted one fails for almost every input.
- Comparing with
==. It compiles and returnsfalseevery time, even for identical contents.
Testing
"anagram","nagaram"givestrue."rat","car"givesfalse."ab","abc"givesfalse."aab","abb"givesfalse."a","a"givestrue.
