← Home

242. Valid Anagram

LeetCode article · C++ solution
Website made by wuisabel-gif · Original C++ code by keineahnung2345
greedyC++Markdown
242

This is one of those problems where the clean idea matters more than the amount of code. For 242. Valid Anagram, the solution in this repository is mainly a greedy solution.

Guide

What?

The code is easier to read if we treat it as a controlled search through possible states. Instead of trying to be clever immediately, read the code as a sequence of questions:

  • What state are we keeping?
  • How do we move from one state to the next?
  • When do we know the answer is already determined?

For this file, the main tools are: greedy.

Guide

When?

Use this approach when the hard part is not syntax, but deciding what must stay true after every update. The accepted code reduces that pressure by storing exactly the information that remains useful later.

The important function names to track are isAnagram.

Guide

Why?

The solution works because it narrows the problem until every update has a clear reason to exist.

  • Sorting is used to make local choices comparable, so the later scan does not have to rediscover order.
  • A map keeps the lookup side cheap; the code pays a little memory to avoid repeated searching.
  • The final return is not magic; it is the invariant after the loops or recursion have finished doing their accounting.

Guide

How?

Walk through the solution in this order:

  1. Start from the smallest reliable state.
  2. Expand one legal move at a time.
  3. Cache, count, or merge information as soon as it becomes settled.
  4. Let the final stored value answer the original question.

The most important competitive-programming habit here is to trust the invariant. Once the invariant is right, the loops become much less scary.

Guide

Complexity

  • Time: O(n) to O(n log n), depending on the dominant loop or data structure operation
  • Space: O(n) in the usual case for auxiliary containers or recursion

Guide

C++ Solution

Your submission

The accepted solution

solution.cpp
01/**
02Given two strings s and t , write a function to determine if t is an anagram of s.
03
04Example 1:
05
06Input: s = "anagram", t = "nagaram"
07Output: true
08Example 2:
09
10Input: s = "rat", t = "car"
11Output: false
12Note:
13You may assume the string contains only lowercase alphabets.
14
15Follow up:
16What if the inputs contain unicode characters? How would you adapt your solution to such case?
17**/
18
19//Runtime: 40 ms, faster than 6.44% of C++ online submissions for Valid Anagram.
20//Memory Usage: 9.6 MB, less than 5.16% of C++ online submissions for Valid Anagram.
21
22class Solution {
23public:
24    bool isAnagram(string s, string t) {
25        map<char, int> count;
26        
27        for(char c : s){
28            if(count.find(c) == count.end()){
29                count[c] = 1;
30            }else{
31                count[c] += 1;
32            }
33        }
34        
35        for(char c : t){
36            map<char, int>::iterator it = count.find(c);
37            if(it == count.end()){
38                return false;
39            }else if(count[c] > 1){
40                count[c] -= 1;
41            }else{
42                count.erase(it);
43            }
44        }
45        
46        if(count.empty()) return true;
47        return false;
48    }
49};
50
51/**
52Approach #1 (Sorting) [Accepted]
53Algorithm
54
55An anagram is produced by rearranging the letters of ss into tt. Therefore, if tt is an anagram of ss, sorting both strings will result in two identical strings. Furthermore, if ss and tt have different lengths, tt must not be an anagram of ss and we can return early.
56
57public boolean isAnagram(String s, String t) {
58    if (s.length() != t.length()) {
59        return false;
60    }
61    char[] str1 = s.toCharArray();
62    char[] str2 = t.toCharArray();
63    Arrays.sort(str1);
64    Arrays.sort(str2);
65    return Arrays.equals(str1, str2);
66}
67Complexity analysis
68
69Time complexity : O(n \log n)O(nlogn). Assume that nn is the length of ss, sorting costs O(n \log n)O(nlogn) and comparing two strings costs O(n)O(n). Sorting time dominates and the overall time complexity is O(n \log n)O(nlogn).
70
71Space complexity : O(1)O(1). Space depends on the sorting implementation which, usually, costs O(1)O(1) auxiliary space if heapsort is used. Note that in Java, toCharArray() makes a copy of the string so it costs O(n)O(n) extra space, but we ignore this for complexity analysis because:
72
73It is a language dependent detail.
74It depends on how the function is designed. For example, the function parameter types can be changed to char[].
75**/
76
77/**
78Approach #2 (Hash Table) [Accepted]
79Algorithm
80
81To examine if tt is a rearrangement of ss, we can count occurrences of each letter in the two strings and compare them. Since both ss and tt contain only letters from a-za−z, a simple counter table of size 26 is suffice.
82
83Do we need two counter tables for comparison? Actually no, because we could increment the counter for each letter in ss and decrement the counter for each letter in tt, then check if the counter reaches back to zero.
84
85public boolean isAnagram(String s, String t) {
86    if (s.length() != t.length()) {
87        return false;
88    }
89    int[] counter = new int[26];
90    for (int i = 0; i < s.length(); i++) {
91        counter[s.charAt(i) - 'a']++;
92        counter[t.charAt(i) - 'a']--;
93    }
94    for (int count : counter) {
95        if (count != 0) {
96            return false;
97        }
98    }
99    return true;
100}
101Or we could first increment the counter for ss, then decrement the counter for tt. 
102If at any point the counter drops below zero, we know that tt contains an extra letter not in ss and return false immediately.
103
104public boolean isAnagram(String s, String t) {
105    if (s.length() != t.length()) {
106        return false;
107    }
108    int[] table = new int[26];
109    for (int i = 0; i < s.length(); i++) {
110        table[s.charAt(i) - 'a']++;
111    }
112    for (int i = 0; i < t.length(); i++) {
113        table[t.charAt(i) - 'a']--;
114        if (table[t.charAt(i) - 'a'] < 0) {
115            return false;
116        }
117    }
118    return true;
119}
120Complexity analysis
121
122Time complexity : O(n)O(n). Time complexity is O(n)O(n) because accessing the counter table is a constant time operation.
123
124Space complexity : O(1)O(1). Although we do use extra space, 
125the space complexity is O(1)O(1) because the table's size stays constant no matter how large nn is.
126
127Follow up
128
129What if the inputs contain unicode characters? How would you adapt your solution to such case?
130
131Answer
132
133Use a hash table instead of a fixed size counter. 
134Imagine allocating a large size array to fit the entire range of unicode characters, 
135which could go up to more than 1 million. A hash table is a more generic solution and could adapt to any range of characters.
136**/

Cost

Complexity

Time
O(n) to O(n log n), depending on the dominant loop or data structure operation
Dominated by the main traversal, recursion, or data-structure operations in the code.
Space
O(n) in the usual case for auxiliary containers or recursion
Auxiliary state plus the answer structure where the problem requires one.