Problem overview
The method isAnagram receives two strings, s and t, and returns a bool: true when t can be formed by rearranging the letters of s, using every letter exactly as many times as it appears, and false otherwise. Both strings hold lowercase English letters and each has at least one and at most 50,000 characters.
For s = "anagram" and t = "nagaram" the answer is true: both contain three as and one each of n, g, r and m. For s = "rat" and t = "car" the answer is false, because t has a c that s lacks.
This lesson asks for O(n log n) time and O(n) extra space for sorted copies of the two strings. Faster counting solutions exist and come in later lessons.
Approach: sorting removes the order
Two words are anagrams when they differ only in the order of their letters. So the order is the one thing to get rid of. Sorting does exactly that: once the letters of each word are in alphabetical order, any two anagrams turn into the same string.
For "anagram" and "nagaram" both sorted strings are "aaagmnr", so they are equal. For "rat" and "car" the sorted strings are "art" and "acr", which differ.
The check is correct because two strings that contain the same letters, each the same number of times, have the same sorted form, and two strings with a different count for some letter have different sorted forms. Strings of different lengths can't match either, since a sorted string keeps its length. So no separate length check is needed.
The cost is dominated by the two sorts, each O(n log n), followed by one linear comparison. The brute-force alternative of trying every rearrangement of one string would be hopelessly slow, and counting letters, shown in a later lesson, reaches O(n).
Step by step
- Sort
sin place withsort(s.begin(), s.end()). The two iterators mark the whole string. The method'ssis its own copy, since the parameter is taken by value, so the caller's string stays as it was. - Sort
tthe same way. Both words must get identical treatment for their sorted forms to be comparable. - Return
s == t. The==operator onstd::stringcompares lengths and then characters, so it istrueonly when both strings hold the same characters in the same order.
The solution in C++
class Solution {
public:
bool isAnagram(string s, string t) {
sort(s.begin(), s.end());
sort(t.begin(), t.end());
return s == t;
}
};
C++ details worth a closer look:
- Iterator ranges.
s.begin()points at the first character ands.end()points one past the last. The range is half-open, sosorthandles everything from the first character through the last. sortworks in place. It rearranges the characters of the string it is given and returns nothing. That is why the solution calls it as a statement and then compares the strings themselves.- By-value parameters. Because
sandtare copies, sorting them can't affect the caller. The copies are also theO(n)extra space the lesson mentions. Withconst string¶meters you would have to copy the strings yourself before sorting. - Characters sort by code. A
charcompares as a small integer, and the lowercase letters have increasing codes from'a'to'z', so sorting gives alphabetical order. ==compares contents. Unlike comparing pointers to C-style strings,==on twostd::stringobjects compares the text they hold.
Complexity
Time: O(n log n). Each sort call takes O(n log n) comparisons in the worst case, and the final comparison is O(n). The sorts dominate.
Space: O(n). The by-value parameters are two copies of the input, and these are the strings that get sorted. std::sort itself needs only a small amount of extra memory.
Edge cases and common mistakes
- Different lengths.
"ab"and"abc"givefalse: the sorted strings have different lengths, so==isfalse. - Same letters, different counts.
"aab"and"abb"contain the same set of letters but not the same counts. Their sorted forms are"aab"and"abb", which differ. A check that only asks whether each letter of one string occurs somewhere in the other would saytruehere. - Identical strings. A string is an anagram of itself, so
"abc"and"abc"returntrue. - Sorting only one string. If only
swere sorted, it would almost never equal an unsortedt. Both must be sorted. - Forgetting that
sortreturns nothing. Writingreturn sort(s.begin(), s.end()) == ...doesn't compile, becausesortreturnsvoid. - Using the end iterator wrongly.
s.end()is already one past the last character. Passings.end() - 1would leave the last character out of the sort.
Testing
s = "anagram",t = "nagaram"givestrue.s = "rat",t = "car"givesfalse.s = "ab",t = "abc"givesfalse.s = "aab",t = "abb"givesfalse.s = "a",t = "a"givestrue.
