排序题目:找不同

题目

标题和出处

标题:找不同

出处: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} 0s.length1000
  • 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 中一定存在一个计数为负,该计数对应的字母即为被添加的字母。

实现方面,可以有以下两点优化。

  1. 由于在遍历 t t t 的过程中, counts \textit{counts} counts 中的计数只会减少,因此当 counts \textit{counts} counts 中的计数出现负数时,该计数对应的字母在 t t t 中的出现次数大于在 s s s 中的出现次数,该字母即为被添加的字母,返回该字母。

  2. 遍历 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 ab=ba

  • 结合律:对于任意整数 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) (ab)c=a(bc)

  • 归零律:对于任意整数 a a a,有 a ⊕ a = 0 a \oplus a = 0 aa=0

  • 恒等律:对于任意整数 a a a,有 0 ⊕ a = a ⊕ 0 = a 0 \oplus a = a \oplus 0 = a 0a=a0=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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值