LeetCode HOT100 - 字母异位词分组

从定义出发不难想到这关键就是怎么去表示这个类,怎么设计这个哈希

比较简单的就是用排序,排完序的作为键

时间复杂度 O(nklogk),因为排序的时间是 klogk

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> mp;
        for (auto i : strs) {
            string tmp = i;
            sort(tmp.begin(), tmp.end());
            mp[tmp].emplace_back(i);
        }
        vector<vector<string>> ans;
        for (auto [_, i] : mp) {
            ans.emplace_back(i);
        }
        return ans;
    }
};

还有一个方案就是记录 cnt
如果是只出现一次的话实际上就直接用 长26 的二进制来作为键就好了

但这里出现不是 01 的,所以就是要看怎么表示了,这里参考题解 https://leetcode.cn/problems/group-anagrams/solutions/520469/zi-mu-yi-wei-ci-fen-zu-by-leetcode-solut-gyoc

不过把 cnt 数组作为 key 和直接计算唯一的哈希值有所不同,也就是

  • 把一个东西“唯一地编码出来”
  • 给一个东西算一个“哈希值”方便查找

是不同的

代码中真正的 key 其实不是哈希值,而是:

array<int, 26> cnt;

比如:

  • "eat"cnt
    [1,0,0,0,1,0,...,1,...]
  • "tea"cnt 也是同一个数组
  • "bat"cnt 就是另一个数组

所以真正区分“是不是同一组异位词”的依据是:

两个 array<int,26> 是否完全一样

哈希值只是快速找到可能的位置

比如有 map:

unordered_map<array<int, 26>, vector<string>, ArrayHash> mp;

这里:

  • key 是 array<int,26>
  • value 是 vector<string>
  • ArrayHash 只是告诉 unordered_map 这个数组该怎么算哈希值

也就是说,哈希函数不是在决定“两个 key 是否相等”,
它只是用来决定:

这个 key 大概要放到哪个桶(bucket)里

于是哈希只要求:

  • 相同 key 的哈希值必须相同
  • 不同 key 的哈希值尽量不同,但允许冲突

于是哈希函数里可以是

int h = 0;
for (int x : arr) {
    h = h * 31 + x;
}

这样的形式,不要求每个字母的出现次数一定小于 31

题解里的

return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
    return (acc << 1) ^ fn(num);
});

也是类似的,操作是:

  • 之前的结果左移一位
  • 再和当前数的哈希值异或起来

它的目标也不是“零冲突唯一编码”,而是:

尽量把数组内容打散,生成一个分布还不错的哈希值

size_t 是无符号整数类型。

无符号整数溢出时,C++ 的行为不是未定义,而是:按模 \(2^k\) 回绕

因此适合干这种“故意让数越滚越大,然后截断”的哈希混合操作。

class Solution {
public:
    struct Hash {
        size_t operator()(const array<int, 26>& arr) const {
            size_t h = 0;
            for (int x : arr) {
                h = h * 31 + x;
            }
            return h;
        }
    };

    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<array<int, 26>, vector<string>, Hash> mp;
        for (auto s : strs) {
            array<int, 26> cnt{};
            for (auto c : s) {
                cnt[c - 'a']++;
            }
            mp[cnt].push_back(s);
        }
        vector<vector<string>> ans;
        for (auto [_, i] : mp) {
            ans.push_back(i);
        }
        return ans;
    }
};
posted @ 2026-04-09 18:21  rdcamelot  阅读(15)  评论(0)    收藏  举报