排序题目:有效的字母异位词

题目

标题和出处

标题:有效的字母异位词

出处:242. 有效的字母异位词

难度

2 级

题目描述

要求

给定两个字符串 s \texttt{s} s t \texttt{t} t,如果 t \texttt{t} t s \texttt{s} s 的字母异位词则返回 true \texttt{true} true,否则返回 false \texttt{false} false

字母异位词是将另一个单词或词组的字母重新排列之后得到的单词或词组,原始单词或词组中的每个字母恰好使用一次。

示例

示例 1:

输入: s   =   "anagram",   t   =   "nagaram" \texttt{s = "anagram", t = "nagaram"} s = "anagram", t = "nagaram"
输出: true \texttt{true} true

示例 2:

输入: s   =   "rat",   t   =   "car" \texttt{s = "rat", t = "car"} s = "rat", t = "car"
输出: false \texttt{false} false

数据范围

  • 1 ≤ s.length,   t.length ≤ 5 × 10 4 \texttt{1} \le \texttt{s.length, t.length} \le \texttt{5} \times \texttt{10}^\texttt{4} 1s.length, t.length5×104
  • s \texttt{s} s t \texttt{t} t 仅包含小写英语字母

进阶

如果输入字符串包含 Unicode 字符应该如何解决?你能否调整你的解法来应对这种情况?

解法一

思路和算法

t t t s s s 的字母异位词时, s s s 中的每个字符在 t t t 中恰好出现一次,因此 s s s t t t 的长度相同。如果 s s s t t t 的长度不同,则 t t t 不是 s s s 的字母异位词,返回 false \text{false} false

由于两个互为字母异位词的字符串包含的字符完全相同,只有顺序可能不同,因此将两个互为字母异位词的字符串排序之后,得到的有序字符串相同。只要将两个字符串排序之后比较是否相同,即可判断两个字符串是否互为字母异位词。

由于 Java 中的 String \texttt{String} String 类型的对象是不可变的,因此需要对 s s s t t t 分别调用 toCharArray \texttt{toCharArray} toCharArray 方法得到 char \texttt{char} char 类型的数组,然后对两个字符数组排序,比较排序后的两个字符数组是否相同。

代码

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        char[] sArr = s.toCharArray();
        char[] tArr = t.toCharArray();
        Arrays.sort(sArr);
        Arrays.sort(tArr);
        return Arrays.equals(sArr, tArr);
    }
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是字符串 s s s 的长度。当 s s s t t t 的长度不同时只需要 O ( 1 ) O(1) O(1) 的时间就能知道不是字母异位词,当 s s s t t t 的长度相同时需要 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 的字母异位词时,同一个字符在 s s s 中的出现次数和在 t t t 中的出现次数相同。因此可以通过统计每个字符在 s s s t t t 中的出现次数判断 t t t 是否是 s s s 的字母异位词。

首先判断 s s s t t t 的长度是否相同,如果长度不同,则 t t t 不是 s s s 的字母异位词,返回 false \text{false} false

由于 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 中的每个计数都是 0 0 0,则每个字符在 s s s t t t 中的出现次数相同, t t t s s s 的字母异位词。

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

  1. 由于在遍历 t t t 的过程中, counts \textit{counts} counts 中的计数只会减少,因此当 counts \textit{counts} counts 中的计数出现负数时,该计数对应的字符在 t t t 中的出现次数大于在 s s s 中的出现次数,此时 t t t 不是 s s s 的字母异位词,返回 false \text{false} false

  2. 如果在遍历 t t t 的过程中, counts \textit{counts} counts 中的计数都没有出现负数,则遍历结束之后可以确定 t t t s s s 的字母异位词,不需要再次遍历 counts \textit{counts} counts 检查是否每个计数都是 0 0 0,可以直接返回 true \text{true} true。理由如下。

    1. 遍历 t t t 的过程中, counts \textit{counts} counts 中的计数都没有出现负数,说明任何字符在 t t t 中的出现次数不超过在 s s s 中的出现次数。

    2. 假设存在一个字符,该字符在 t t t 中的出现次数小于在 s s s 中的出现次数,则 t t t 的长度小于 s s s 的长度,与 t t t 的长度等于 s s s 的长度矛盾。因此任何字符在 t t t 中的出现次数都等于在 s s s 中的出现次数, t t t s s s 的字母异位词。

代码

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        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 false;
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。当 s s s t t t 的长度不同时只需要 O ( 1 ) O(1) O(1) 的时间就能知道不是字母异位词,当 s s s t t t 的长度相同时需要遍历 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(∣Σ∣)

解法三

思路和算法

对于进阶问题,如果输入字符串包含 Unicode 字符,则字符集不再局限于小写英语字母,因此不能使用数组维护计数,而是需要使用哈希表维护计数。

对于 Unicode 字符的处理,可以使用解法二的思路,将数组换成哈希表即可。

代码

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        Map<Character, Integer> counts = new HashMap<Character, Integer>();
        int length = s.length();
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            counts.put(c, counts.getOrDefault(c, 0) + 1);
        }
        for (int i = 0; i < length; i++) {
            char c = t.charAt(i);
            counts.put(c, counts.getOrDefault(c, 0) - 1);
            if (counts.get(c) < 0) {
                return false;
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。当 s s s t t t 的长度不同时只需要 O ( 1 ) O(1) O(1) 的时间就能知道不是字母异位词,当 s s s t t t 的长度相同时需要遍历 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(∣Σ∣)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伟大的车尔尼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值