242. 有效的字母异位词(Valid Anagram)

频次 ★★★ · 难度 🟢 · 高频:全厂

题目

给定两个字符串 st,判断 t 是否是 s 的字母异位词(两个字符串中每个字符的出现次数完全相同)。

示例

输入: s = "anagram", t = "nagaram"
输出: true

输入: s = "rat", t = "car"
输出: false

说明:假设字符串只包含小写字母。

思路

字母异位词的本质是字符计数相同。因为限定小写字母(26 个),用一个 int[26] 数组当哈希表:遍历 s 时对应位置 +1,遍历 t 时对应位置 -1,最后检查数组是否全 0。全 0 说明两个字符串每个字符出现次数相同,互为异位词。

如果字符串包含 Unicode 字符(不限小写字母),改用 HashMap<Character, Integer>,思路不变。

代码

public boolean isAnagram(String s, String t) {
    if (s.length() != t.length()) {
        return false;
    }
    int[] count = new int[26];
    for (int i = 0; i < s.length(); i++) {
        count[s.charAt(i) - 'a']++;
        count[t.charAt(i) - 'a']--;
    }
    for (int c : count) {
        if (c != 0) {
            return false;
        }
    }
    return true;
}

复杂度

  • 时间:O(n) — 两个字符串各遍历一次,最后检查 26 个位置是常数时间
  • 空间:O(1) — int[26] 是固定大小,不随输入规模增长

边界条件

  • 两个字符串长度不等:直接返回 false,不可能互为异位词
  • 空字符串:两个空字符串互为异位词,计数数组全 0,返回 true
  • 包含重复字符:计数 +1/-1 抵消,只要所有字符出现次数相同即可

变式

  • Unicode 字符版:用 HashMap<Character, Integer> 代替 int[26]s 字符 +1,t 字符 -1,最后检查所有 value 是否为 0
  • 只遍历一次计数数组:可以边遍历边维护一个 diff 计数器(s 字符 +1 时若 count 从 0 变 1 则 diff++,t 字符 -1 时若 count 从 0 变 -1 则 diff++,反之则 diff—),最后判断 diff 是否为 0,省去最后检查 26 个位置的循环

易错点

  • 不能只判断 s 的字符都在 t 中出现——还要保证次数相同,如 s="aab", t="abb" 只判断存在性会误判
  • 排序后比较字符串(Arrays.sort)也是 O(n log n),且需要额外空间存储排序结果,不如计数法 O(n) 高效
  • 不能在循环外单独遍历 t 再遍历 s——两个循环分开写会多一次遍历且代码冗余;一次循环同时处理两个字符串的对应字符更简洁

面试追问

  • 如果字符串包含 Unicode 字符怎么办? int[26] 不够用,改用 HashMap<Character, Integer>,时间复杂度仍是 O(n),空间变为 O(k)(k 为字符集大小)。进阶可以讨论 int[128](ASCII)和 int[256](扩展 ASCII)的适用场景。
  • 计数法和排序法各有什么适用场景? 计数法 O(n)/O(1) 适合小字符集;排序法 O(n log n) 实现简单,适合字符集很大但字符串很短的情况(此时 k 可能超过 n,计数法空间反而更大)。

关联题