文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法一
- 思路和算法
- 代码
- 复杂度分析
- 解法二
- 思路和算法
- 代码
- 复杂度分析
- 解法三
- 思路和算法
- 代码
- 复杂度分析
- 解法四
- 预备知识
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:找不同
出处:389. 找不同
难度
2 级
题目描述
要求
给定两个字符串 s \texttt{s} s 和 t \texttt{t} t。
字符串 t \texttt{t} t 的生成方法是将字符串 s \texttt{s} s 随机重排,然后在随机位置添加一个字母。
请找出在 t \texttt{t} t 中被添加的字母。
示例
示例 1:
输入: s = "abcd", t = "abcde" \texttt{s = "abcd", t = "abcde"} s = "abcd", t = "abcde"
输出: "e" \texttt{"e"} "e"
解释: ‘e’ \texttt{`e'} ‘e’ 是被添加的字母。
示例 2:
输入: s = "", t = "y" \texttt{s = "", t = "y"} s = "", t = "y"
输出: "y" \texttt{"y"} "y"
数据范围
- 0 ≤ s.length ≤ 1000 \texttt{0} \le \texttt{s.length} \le \texttt{1000} 0≤s.length≤1000
- t.length = s.length + 1 \texttt{t.length} = \texttt{s.length} + \texttt{1} t.length=s.length+1
- s \texttt{s} s 和 t \texttt{t} t 只包含小写英语字母
解法一
思路和算法
由于 t t t 比 s s s 多一个字母,其余字母都相同只是顺序可能不同,因此将两个字符串排序之后,除了多出的一个字母以外,两个有序字符串相同。
同时遍历两个有序字符串,当遇到第一个不同的字母时, t t t 中该位置的字母即为被添加的字母。如果遍历字符串 s s s 结束时没有遇到不同的字母,则被添加的字母是 t t t 的最后一个字母。
代码
class Solution {public char findTheDifference(String s, String t) {char[] sArr = s.toCharArray();char[] tArr = t.toCharArray();Arrays.sort(sArr);Arrays.sort(tArr);int length = sArr.length;for (int i = 0; i < length; i++) {if (sArr[i] != tArr[i]) {return tArr[i];}}return tArr[length];}
}
复杂度分析
-
时间复杂度: O ( n log n ) O(n \log n) O(nlogn),其中 n n n 是字符串 s s s 的长度。需要 O ( n log n ) O(n \log n) O(nlogn) 的时间对两个字符数组排序,排序后需要 O ( n ) O(n) O(n) 的时间找出被添加的字母,因此时间复杂度是 O ( n log n ) O(n \log n) O(nlogn)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要创建两个长度为 n n n 的字符数组,排序需要 O ( log n ) O(\log n) O(logn) 的递归调用栈空间,因此空间复杂度是 O ( n ) O(n) O(n)。
解法二
思路和算法
由于 t t t 比 s s s 多一个字母,其余字母都相同只是顺序可能不同,因此只有一个字母在 t t t 中的出现次数比在 s s s 中的出现次数多 1 1 1 次,其余字母在 t t t 中的出现次数和在 s s s 中的出现次数相同。只要统计每个字母在 s s s 和 t t t 中的出现次数,即可找出被添加的字母。
由于 s s s 和 t t t 只包含小写英语字母,因此可以创建长度为 26 26 26 的数组 counts \textit{counts} counts 记录每个字母的计数。
首先遍历 s s s,对于每个字母,将其在 counts \textit{counts} counts 中对应的计数加 1 1 1。然后遍历 t t t,对于每个字母,将其在 counts \textit{counts} counts 中对应的计数减 1 1 1。遍历 s s s 和 t t t 结束之后, counts \textit{counts} counts 中一定存在一个计数为负,该计数对应的字母即为被添加的字母。
实现方面,可以有以下两点优化。
-
由于在遍历 t t t 的过程中, counts \textit{counts} counts 中的计数只会减少,因此当 counts \textit{counts} counts 中的计数出现负数时,该计数对应的字母在 t t t 中的出现次数大于在 s s s 中的出现次数,该字母即为被添加的字母,返回该字母。
-
遍历 t t t 的过程中可以省略最后一个字母。如果在遍历 t t t 的过程中, counts \textit{counts} counts 中的计数都没有出现负数,则被添加的字母是 t t t 的最后一个字母。
代码
class Solution {public char findTheDifference(String s, String t) {int[] counts = new int[26];int length = s.length();for (int i = 0; i < length; i++) {char c = s.charAt(i);counts[c - 'a']++;}for (int i = 0; i < length; i++) {char c = t.charAt(i);counts[c - 'a']--;if (counts[c - 'a'] < 0) {return c;}}return t.charAt(length);}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历 s s s 和 t t t 各一次并维护计数,每次遍历都需要 O ( n ) O(n) O(n) 的时间。
-
空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣),其中 Σ \Sigma Σ 是字符集,这道题中字符集为全部小写英语字母, ∣ Σ ∣ = 26 |\Sigma| = 26 ∣Σ∣=26。需要使用数组记录每个字符的计数,空间为 O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣)。
解法三
思路和算法
可以分别计算 s s s 和 t t t 的每个字母的 ASCII 码之和, t t t 的 ASCII 码之和减去 s s s 的 ASCII 码之和得到的差即为 t t t 中被添加的字母对应的 ASCII 码,根据该 ASCII 码即可得到被添加的字母。
代码
class Solution {public char findTheDifference(String s, String t) {int sSum = 0, tSum = 0;int length = s.length();for (int i = 0; i < length; i++) {sSum += s.charAt(i);}for (int i = 0; i <= length; i++) {tSum += t.charAt(i);}return (char) (tSum - sSum);}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历 s s s 和 t t t 各一次并计算 ASCII 码之和,每次遍历都需要 O ( n ) O(n) O(n) 的时间。
-
空间复杂度: O ( 1 ) O(1) O(1)。
解法四
预备知识
该解法涉及到位运算中的按位异或运算。用 ⊕ \oplus ⊕ 表示按位异或运算符,按位异或运算具有以下性质。
-
交换律:对于任意整数 a a a 和 b b b,有 a ⊕ b = b ⊕ a a \oplus b = b \oplus a a⊕b=b⊕a。
-
结合律:对于任意整数 a a a、 b b b 和 c c c,有 ( a ⊕ b ) ⊕ c = a ⊕ ( b ⊕ c ) (a \oplus b) \oplus c = a \oplus (b \oplus c) (a⊕b)⊕c=a⊕(b⊕c)。
-
归零律:对于任意整数 a a a,有 a ⊕ a = 0 a \oplus a = 0 a⊕a=0。
-
恒等律:对于任意整数 a a a,有 0 ⊕ a = a ⊕ 0 = a 0 \oplus a = a \oplus 0 = a 0⊕a=a⊕0=a。
思路和算法
除了被添加的字母以外,其余的每个字母在 s s s 中的出现次数与在 t t t 中的出现次数相同,因此在 s s s 和 t t t 中的总出现次数是偶数。被添加的字母在 t t t 中的出现次数比在 s s s 中的出现次数多 1 1 1 次,因此在 s s s 和 t t t 中的总出现次数是奇数。
利用按位异或运算的性质,对 s s s 和 t t t 中的所有字母的 ASCII 码计算按位异或的结果。所有出现偶数次的字母经过按位异或运算之后的结果是 0 0 0,只有出现奇数次的字母在按位异或运算之后保留,因此 s s s 和 t t t 中的所有字母的 ASCII 码的按位异或的结果即为 t t t 中被添加的字母对应的 ASCII 码,根据该 ASCII 码即可得到被添加的字母。
代码
class Solution {public char findTheDifference(String s, String t) {int xor = 0;int length = s.length();for (int i = 0; i < length; i++) {xor ^= s.charAt(i);}for (int i = 0; i <= length; i++) {xor ^= t.charAt(i);}return (char) xor;}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历 s s s 和 t t t 各一次并计算按位异或的结果,每次遍历都需要 O ( n ) O(n) O(n) 的时间。
-
空间复杂度: O ( 1 ) O(1) O(1)。