一、引言
最近几天工作很忙,好不容易闲下来了,来看看这道“惬意”的小题:
Given two strings s and t which consist of only lowercase letters.
String t is generated by random shuffling string s and then add one more letter at a random position.
Find the letter that was added in t.
Example:
Input:
s = “abcd”
t = “abcde”Output:
eExplanation:
‘e’ is the letter that was added.
看起来英文解释好像比较多,但是结合实例我们来看看,其实也就是一句话就可以讲清楚的题:s 和 t 都是只含有小写字母的字符串,其中 t 中的内容是 s 中的所有的字母打乱后再随机添加了一个字母,让你找到这个随机添加的字母。
看到这道题,浮现在我脑海里的第一想法有很多,接下来我们来慢慢梳理。
二、使用 std::map
首先,在我们明确了题意之后,我们能够明白一点,这里有两个数组,并且我们需要找到 t 数组相对于 s 数组中多出来的那一个。
那么该怎么做呢?
很容易就会想到 map 对不对?
是啊,我们只需要遍历 s 出一个 s_map,遍历 t 出一个 t_map, 那么再遍历元素多一个的 t 数组,取出每个元素来分别在 s_map 和 t_map 中查询出现次数,出现次数不同的字母即为多添加的字母。
说的已经很详细了,代码如下:
// my solution 1 , runtime = 6 ms
class Solution1 {
public:
char findTheDifference(string s, string t) {
map<char, int> s_map;
map<char, int> t_map;
for (auto c : s) {
++s_map[c];
}
for (auto c : t) {
++t_map[c];
}
for (auto c : t) {
if (t_map[c] != s_map[c]) {
return c;
}
}
}
};
我们提交后,看到 runtime 为 6 ms。
接下来,我们来一起想想,如何降低 runtime?
比如将有序的 map 更换为无序的 unordered_map 试试:
// my solution 2 , runtime = 6 ms
class Solution2 {
public:
char findTheDifference(string s, string t) {
unordered_map<char, int> s_map;
unordered_map<char, int> t_map;
for (auto c : s) {
++s_map[c];
}
for (auto c : t) {
++t_map[c];
}
for (auto c : t) {
if (t_map[c] != s_map[c]) {
return c;
}
}
}
};
非常遗憾,这里发现 runtime 是一样的,可能是根据具体的 case 有关,很显然这里使用这个方法降低 runtime 是不可行的。
那么,让我们想想其他的办法。
三、使用 std::set
这时候,我想到了 std::set ,我记得 std::set 好像有一个 count 方法,我们来看看可不可以降低 runtime:
// my solution 4 , failed
// std::set::count or std::unordered_set::count
// value only equals to 0 or 1
class Solution4 {
public:
char findTheDifference(string s, string t) {
set<char> s_set(s.cbegin(), s.cend());
set<char> t_set(t.cbegin(), t.cend());
for (auto c : t) {
if (s_set.count(c) != t_set.count(c)) {
return c;
}
}
}
};
编译代码,发现 case 跑出了错误!
为什么呢?我们像上面的处理 std::map 的逻辑一样,怎么会出错了呢?
我查找了下 C++ 在线手册,才发现原来 std::set::count 方法的返回值只能为 0 或者 1。也就是说,我们这里想要通过 std::set 来计数字母出现次数的做法,是错误的。
那么,我们该如何降低 runtime 呢?
四、char 其实是个数字而已
要想降低 runtime ,就必须更换思路,上面的思路我们已经走到了性能极限了。
让我们想想,char ,char, char 类型的字母…
想到了什么没?
XOR!Is always the trick!
我在做 LeetCode 第 136 题 Single Number 的时候,当看到最优解的时候内心的想法跟这句话是一样的!(想要了解这道题的仁兄可以点击这里LeetCode之路:136. Single Number)。
对呀!我们可以使用异或啊!
想想,s 中的字母在 t 中全部都有,那么 s 中的字母跟 t中的字母全部异或后,结果肯定为 0 异或t中增加的那个字母值。
代码很简单:
// my solution 5 , perfect runtime = 3 ms
class Solution5 {
public:
char findTheDifference(string s, string t) {
char c = '\0';
for (auto s_c : s) {
c ^= s_c;
}
for (auto t_c : t) {
c ^= t_c;
}
return c;
}
};
看了看 Discuss
,看到了最高票答案也是这个思路,深深的自豪感油然而生。
只要我们认认真真做每一道题,认认真真思考每一道题的最优解,那么总有一天,我们也会做出来让别人瞠目结舌的最优解!
五、总结
这确实是一道非常简单的题目,最优解使用了 XOR 异或,不过中间从 std::map 到 std::unordered_map 的尝试,从 std::unordered_map 再到 std::set 的尝试,无一不是在夯实自己的逻辑思维和编程基础。
永远保持着对于最优解的好奇心;
永远保持着对于奇妙解法的探索心;
总有一天,我们都会成为别人眼中的大神的!
I code,I fly!
I believe!