Problem overview
findTheDifference(s, t) takes two strings of lowercase letters. The string t is s shuffled with one extra letter added somewhere, and the function returns that letter as a Char.
With s = "abcd" and t = "abcde" the result is 'e', and with s = "" and t = "y" it is 'y'. This lesson requires O(n) time and O(1) extra space, where n is the length of t. That rules out a map of counts, which needs space for the letters, and points to a trick with bits.
Approach: let equal letters cancel with XOR
Every character has a numeric code. In Kotlin, c.code gives it as an Int: 'a'.code is 97, 'b'.code is 98 and 'e'.code is 101.
The bitwise XOR operation combines two numbers bit by bit, giving a 1 where the bits differ and a 0 where they match. Three of its properties solve this problem:
- A number XOR itself is 0, because every bit matches:
5 xor 5is 0. - A number XOR 0 is unchanged:
5 xor 0is 5. - The order doesn't matter, so you can group numbers freely.
Now picture XOR-ing the codes of all letters of s and then all letters of t. Each letter of s has an identical partner in t, and the order of the letters is irrelevant, so each pair cancels to 0. One letter of t has no partner. Everything else cancels, and what is left is the code of the extra letter. Converting that number back to a Char gives the answer.
Take s = "ab" and t = "bab". XOR-ing 97, 98 from s and 98, 97, 98 from t gives the same value as pairing them up: 97 cancels with 97, one 98 cancels with the other 98, and the last 98 is left. The function returns 'b'. Duplicates are no problem, since an extra copy of a letter that s already has simply remains unpaired.
Unlike the counting approaches, this one doesn't care how large the alphabet is, and it needs no table at all.
Step by step
- Declare the accumulator.
var result = 0is anIntthat every code will be folded into. It starts at 0 because XOR with 0 changes nothing. - Loop over
s.for (c in s)visits each letter. - Fold each code in.
result = result xor c.codeupdates the running value. - Loop over
t. The same loop and the same fold run over the second string. - Fold in
t's codes. Each letter that appears in both strings has now been XOR-ed in twice. - Convert and return.
result.toChar()turns the leftover code back into a letter.
The solution in Kotlin
class Solution {
fun findTheDifference(s: String, t: String): Char {
var result = 0
for (c in s) {
result = result xor c.code
}
for (c in t) {
result = result xor c.code
}
return result.toChar()
}
}
Kotlin details worth a look:
xoris a function, not a symbol. Kotlin has no^operator for bits.xoris an infix function on the integer types, soa xor bis the same asa.xor(b).Charhas noxor. Writing'a' xor 'b'fails withunresolved reference 'xor'. You first take the numeric value with.code.CharandIntare different types. Kotlin never converts between them silently.c.codegoes fromChartoInt, andtoChar()goes back. Without the final conversion the function wouldn't match its return type.- The result is always a valid letter. Because the only value left is the code of one lowercase letter, between 97 and 122,
toChar()loses nothing. - No fallback return is needed. Unlike the loop-and-return solutions, this one ends with a single return that always runs.
Complexity
Time: O(n). One pass over s and one over t, each step a single XOR.
Space: O(1). The function keeps one integer and a loop variable, whatever the length of the strings.
Edge cases and common mistakes
- Empty
s. Only the one letter oftis folded in, soresultequals its code and"y"returns'y'. - Duplicate letters.
s = "aab"andt = "baab"returns'b': bothapairs cancel, as do two of the threebletters. - Skipping one of the loops. XOR-ing only
tleaves the XOR of all the letters oft, which is not the extra one unlesssis empty. Both strings must be folded in. - Using the wrong operator. Plain
andorordoesn't cancel equal numbers:5 and 5and5 or 5are both 5, not 0. XOR is the operation whose result for equal inputs is 0. - Forgetting
.codeortoChar(). The compiler reports a type mismatch betweenCharandInt.
Testing
s = "abcd",t = "abcde"gives'e'.s = "",t = "y"gives'y'.s = "aab",t = "baab"gives'b'.
