题目描述

✅ LCR 033. 字母异位词分组

image-20260928235358376

image-20260928235358377

题意分析

将每种字符出现次数完全相同的字符串分到一组。这里允许字符串内容完全相同,重复输入也要按出现次数保留,不能套用 LCR 032 排除相同字符串的条件。

每个字符串只含小写字母,也可能为空。结果顺序不限,因此只需找到可靠的分组依据,不必额外整理各组的输出顺序。

解法:排序签名分组

核心思路

[!blue]

与其逐对比较字符串,可以为每个字符串计算一个只由字符组成决定的签名。将字符排序后拼成字符串,就得到这样的签名:排序消除了原始顺序,但保留了每种字符的数量。

字符频次相同的两个串排序后一定相同;反过来,排序结果相同也说明每个字符的次数相同。这个双向关系保证同类不会被分开,不同类也不会混到一起。

用哈希表 d 保存“签名 → 这一组的原字符串列表”。逐个计算签名,将原串追加到对应列表;没有这个签名时先建立空组。处理结束后,表中的每个列表就是一个完整分组。

签名只作为分类键,返回值保留原字符串本身。重复字符串会被重复追加,空字符串的签名仍是空字符串,这些边界都不需要专门分支。

解题步骤

  1. 创建签名到字符串列表的哈希表。
  2. 对每个原串 s,复制字符并排序,再将排序结果转成签名 k。Java 使用字符数组,Go 在小写字母约束下使用字节切片。
  3. 若签名尚无分组,创建分组;随后追加原串 s,不能追加签名 k。
  4. 将哈希表的全部分组收集成结果,无需按签名或原串再排序。

代码实现

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        // 键是排序后的签名,值是该签名对应的全部原字符串。
        Map<String, List<String>> d = new HashMap<>();

        for (String s : strs) {
            char[] t = s.toCharArray();

            // 排序抹掉顺序、保留字符组成,正是变位词的规范形式。
            Arrays.sort(t);
            String k = String.valueOf(t);

            // 存进去的必须是原串 s,签名只用于分组。
            d.computeIfAbsent(k, key -> new ArrayList<>()).add(s);
        }

        return new ArrayList<>(d.values());
    }
}
import (
    "sort"
)

func groupAnagrams(strs []string) (answer [][]string) {
    // 键是排序后的签名,值是该签名对应的全部原字符串。
    d := map[string][]string{}
    for _, s := range strs {
        t := []byte(s)
        // 排序抹掉顺序、保留字符组成,正是变位词的规范形式。
        sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
        k := string(t)
        // 存进去的必须是原串 s,签名只用于分组。
        d[k] = append(d[k], s)
    }
    for _, v := range d {
        answer = append(answer, v)
    }
    return
}

复杂度分析

设字符串数为 n,最大字符串长度为 L。

  • 时间复杂度:$O(n(L\log(L+1)+1))$,每个字符串排序并构造签名,哈希键计算也需要读取签名;额外的常数项包含空字符串及收集分组的开销。
  • 空间复杂度:$O(n(L+1))$,签名总长度最多为 $nL$,分组保存 n 个原串引用;单次排序另需 $O(L)$ 的字符副本。

关键点总结

[!green]

  • 排序后的签名完整保留字符频次,与原始排列顺序无关。
  • 同组当且仅当签名相同,用哈希表即可一次遍历完成归组。
  • 分类键是中间数据,组内保存原字符串及其每次出现。

易错点总结

[!yellow]

  • 相同字符串属于同一组,不能因为内容一致就排除或去重。
  • 返回的是原串,不是排序后的签名;应当排序字符副本,再把原串追加到组内。
  • 字符集合相同不够,重复次数也必须相同,签名必须保留这些次数。
  • 空字符串也有合法的空签名,应正常进入对应分组。

相似题目

题目 难度 关联与区别
242. 有效的字母异位词 简单 分组键来自两串互为异位词的判定条件,本题需要把全部同类字符串聚合。
438. 找到字符串中所有字母异位词 中等 同样使用频次特征,原题在定长窗口中增量更新,本题对完整单词生成签名。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/91362306
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!