Problem overview
The string t is the string s shuffled, with one extra lowercase letter inserted somewhere. findTheDifference returns that extra letter as a char. The string s may be empty, and t is always one character longer than s.
For s = "abcd" and t = "abcde" the answer is 'e': the letters a to d each appear once in both strings, and e has no partner. For s = "" and t = "y" the answer is 'y'.
This lesson asks for O(n) time, where n is the length of t.
Approach: build a frequency map, then spend it
The brute-force way recounts a letter in both strings for every character of t, which is O(n²). The slow part is the repeated counting. Counting every letter of s once, up front, removes it.
Walk s once and store, for each letter, how many copies it has. This is a frequency map. Then walk t and treat each stored copy as a token: each letter of t uses up one copy of itself. Because t is a shuffle of s plus one letter, every letter finds a token except the extra one. When a letter of t finds its count at 0 (or finds nothing at all), it is the extra letter.
Take s = "aab" and t = "abaa". The map after the first loop is a: 2, b: 1. Walking t: the first a uses one token (a: 1), b uses its only token (b: 0), the second a uses the last a token (a: 0), and the third a finds 0, so 'a' is returned. This also shows why a plain "is it in s?" check isn't enough: the extra letter can be a duplicate of a letter that is in s.
The cost is two passes over the strings, each step a hash lookup, so O(n) on average. A later lesson removes the map entirely with XOR.
Step by step
- Declare an
unordered_map<char, int>namedcounter. The keys are letters and the values are how many copies of each remain. - Loop over
swith a range-basedfor, naming the characterc. - Increment
counter[c]. A missing key is created with the value 0 byoperator[], so the first sight of a letter works without a special case. - Loop over
t, again naming the characterc. - Check whether
counter[c] == 0. That covers both a letter that isn't in the map and one whose copies are all used up. - Return
cinside thatif: it is the extra letter. - Otherwise decrement
counter[c]. This line is reached only when a copy exists, so a count never goes below zero. - Return a fallback character after the loop. The constraints guarantee that the loop returns first, but a function with a return type must return on every path.
The solution in C++
class Solution {
public:
char findTheDifference(string s, string t) {
unordered_map<char, int> counter;
for (char c : s) {
counter[c]++;
}
for (char c : t) {
if (counter[c] == 0) {
return c;
}
counter[c]--;
}
return ' ';
}
};
C++ details worth a closer look:
operator[]inserts. Readingcounter[c]for a missing key addscwith the value 0. That is what makescounter[c]++work on the first sight of a letter. It also means the checkcounter[c] == 0in the second loop may insert an entry, which is harmless here because the function returns right away. When you want to look without inserting, usefindorcount.- Values are zero-initialised. A new
intvalue created byoperator[]is 0, so you never read garbage. - Two loops, two different
cvariables. Eachchar cis scoped to its own loop, so reusing the name is legal. - The strings are copies.
sandtare taken by value, which is fine at 1000 characters. Nothing in the method changes them. - The fallback. Running off the end of a non-void function is undefined behaviour, so the final
return ' ';closes the path even though valid input never reaches it.
Complexity
Time: O(n) on average. The first loop runs n - 1 times and the second at most n times. Each iteration does a constant number of hash-map operations, which cost O(1) on average.
Space: O(1) in practice, O(k) in general. The map has one entry per distinct letter in s, and the input holds only lowercase letters, so there are at most 26 entries. Counting the map as proportional to the number of distinct characters is the precise statement.
Edge cases and common mistakes
- Empty
s. The map stays empty, the first letter oftreads 0, and it is returned. - The extra letter repeats a letter of
s. Withs = "a"andt = "aa"the firstauses the only token and the second finds 0, so the answer is'a'. - Decrementing before checking. If the decrement came first, the counts would go negative and the zero test would never fire for a duplicate. Check first, then decrement.
- Counting
tin the first loop. The map must be built fromsonly. Counting both strings into the same map would never produce a zero for the extra letter. - Returning inside the first loop. The extra letter is only discovered while walking
t.
Testing
s = "abcd",t = "abcde"gives'e'.s = "",t = "y"gives'y'.s = "aab",t = "abaa"gives'a'.s = "xyz",t = "zyxq"gives'q'.
