Problem overview
The string t is s shuffled with one extra lowercase letter inserted. The method findTheDifference(s: String, t: String): Char must return that letter: 'e' for s = "abcd" and t = "abcde", and 'y' for s = "" and t = "y".
The lesson asks for O(n) time and O(1) extra space. Counting letters in a map is O(n) time too, but it needs a structure that holds the counts. XOR needs one integer.
Approach: letters that appear twice cancel out
Every character is a number, its character code. XOR, written ^ in Scala, combines two integers bit by bit: a result bit is 1 when the two input bits differ. Three properties matter:
x ^ x = 0: a number XOR itself is zero.x ^ 0 = x: XOR with zero changes nothing.- The order of the operands doesn't matter, and neither does how they are grouped.
Start with 0 and XOR in the code of every character of s, then of every character of t. Every letter of s has an equal partner in t, so the codes come in matching pairs. Because of the order-free property, the pairs can be imagined next to each other, and each pair gives 0. The one letter without a partner is the extra one, and it is what remains. Turning that number back into a Char gives the answer.
Take s = "aab" and t = "baab". The values XORed are a, a, b, b, a, a, b in some order: a appears four times and b three times. The four a values cancel in pairs, two of the b values cancel, and one b is left, so the result is the code of 'b'. Duplicates are no problem: an extra copy of a letter simply makes its total count odd.
Step by step
- Declare an accumulator.
var result = 0is anInt, and it starts at 0 because0 ^ x = xleaves the first value unchanged. - Loop over
s. The loop variablecis each character. - XOR each character in.
result ^= ccombines the accumulator with the character's code. ACharwidens toIntfor the operation, and the result stays anInt. - Loop over
t. Again the variable isc. - XOR those in too. After this loop, every letter that occurs in both strings has been XORed an even number of times and has cancelled. Only the extra letter, XORed one time more than its partners, survives.
- Convert back.
result.toCharturns the integer code into theCharthe method returns.
The solution in Scala
class Solution {
def findTheDifference(s: String, t: String): Char = {
var result = 0
for (c <- s) {
result ^= c
}
for (c <- t) {
result ^= c
}
return result.toChar
}
}
The Scala details worth a closer look:
CharandInt. In Scala aCharis a 16-bit unsigned number that is shown as a character.'a' ^ 'b'is anInt(the value 3), andresult ^= cworks because the compiler widenscto anIntfirst. The accumulator has to be anInt.^=. It is the compound assignment forresult = result ^ cand requiresresultto be avar.toChar. Going fromIntback toCharis not automatic, because it can lose information.(97 ^ 97 ^ 101).toChargives'e'. Here the value is always a valid lowercase letter code, so nothing is lost.- Loops over strings.
for (c <- s)walks aStringcharacter by character, givingCharvalues. returnin the method. The finalreturnis not inside a loop, so it is an ordinary return. The method's last expression,result.toChar, would give the same value without the keyword.
Complexity
Time: O(n). The loops visit n - 1 and n characters, with one XOR each. XOR on an Int takes constant time.
Space: O(1). One Int accumulator, plus the loop variable. The method builds no map, array or copy of the strings.
Edge cases and common mistakes
- Empty
s. The first loop does nothing, and the second XORs the single character oftinto 0. The result is that character's code, sos = ""andt = "y"give'y'. - A duplicate extra letter. For
s = "z"andt = "zz", the threezvalues leave onez, and the method returns'z'. No special handling is needed. - Returning the number. The method must return a
Char. Leaving offtoChargives a compile error,Found: (result : Int)andRequired: Char. - Starting from a nonzero value. The accumulator must start at 0. Any other start value stays in the result, and
toCharwould return the wrong letter. - Adding instead of XOR. Using
+does not cancel anything. Summing the codes oftand subtracting the sum forswould also work, but XOR cannot overflow and so it is the cleaner tool. - Only for exactly one extra letter. XOR works because all other letters come in pairs. If the input broke that rule, the result would be meaningless.
Testing
s = "abcd",t = "abcde"returns'e'.s = "",t = "y"returns'y'.s = "aab",t = "baab"returns'b'.s = "z",t = "zz"returns'z'.
