49. Group Anagrams(字母异位词分组)两种解法(C++ & 注释)

本文介绍了一种算法,用于解决将字符串数组中的字母异位词进行分组的问题。通过使用哈希表,包括排序、计数数组和质数相乘三种方法,实现了高效的字母异位词匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 题目描述

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]

说明:

  • 所有输入均为小写字母。
  • 不考虑答案输出的顺序。

题目链接:中文题目英文题目

2. 哈希表(Hash Table)

这题主要的思路是使用哈希表来映射每个字符串,把字母异位词映射到一起,所以一共有三种思路:1)使用sort把所有字符串按照字母表的顺序进行排序;2)把每个字符映射到数组中,以这个数组来进行映射;3)使用质数相乘唯一性来映射字符串。第三种方法我们单独提出来讲解,这里先讲讲第一种和第二种方法。另外,三种方法的代码都合并到“4. 实例代码”里面了。

2.1 使用Sort的哈希表

使用sort直接按字母表顺序排序(即字母从小到大进行排序)是最直接的方法了,比如下面这个例子:

abc -> abc
acb -> abc
abc -> abc
bac -> abc

上面几个字符串排序以后都可以映射到同一个字符串"abc":,我们用排序后的字符串作为key,string数组作为value即可解决问题啦,so easy~

2.2 使用count数组的哈希表

除了使用排序进行映射,我们还可以使用数组记录字符串中每个字符的数目来进行映射,举个例子哒:

abc -> [1,1,1,后面的0省略]
acb -> [1,1,1,后面的0省略]
abc -> [1,1,1,后面的0省略]
bac -> [1,1,1,后面的0省略]

我们知道字母在计算机也是用数字来表示的,比如’a’用数字97表示,所以我们用小写字母减去’a’,就能把字母映射到一个长度为26的数组之中(这个方法在字符串题目中非常常用,大家可以用小本本记下啦),然后我们计数每个字母出现的次数,然后把26个位置的计数都转化成一个字符串作为key即可。比如上面的例子,只有0,1,2的位置为1,其他都为0,所以这些字符串可以映射成:

[1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] -> “11100000000000000000000000”

我看到其他同样的解法里面,会在数字前面加’#’,写成“#1#1#1…”,不是很懂这样做的理由,我这里实际提交的代码没有加其他任何字符,也是可以通过测试的,可能是其他语言有什么问题需要添加其他字符用于区别。

3. 质数相乘(Prime Multiplication)

这里需要用到一个数学知识:

每个自然数,除了质数本身,都可以分解成质数的乘积。

我们可以反过来利用质数不可分解性,所以不同的质数的乘积是唯一的,因而和上面把字母映射到连续数组进行计数不同,这次我们把字母映射到26个不同的质数:

[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103 ]

然后用它们的乘积表示key,比如:

abc -> 2 * 3 * 5 = 30
acb -> 30
bac -> 30
———————————
bcd -> 3 * 5 * 7 = 105
bdc -> 105
dcb -> 105

最后我们需要注意,这样用乘积表示key,如果用带符号的long和int会溢出,所以这里我们用无符号的unisgned long来表示乘积,避免结果溢出。

4. 实例代码

class Solution {
    unordered_map<string, vector<string>> m;
    vector<vector<string>> ans;

    vector<int> primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103 };
    unordered_map<unsigned long, vector<string>> m1;

    // 下面三个方法选一个即可
    // (1)使用sort的哈希表
    void usingSort(string& str) {
        string temp = str;
        sort(temp.begin(), temp.end());
        if (m.count(temp)) m[temp].push_back(str);
        else m.insert(pair<string, vector<string>>(temp, { str }));
    }

    // (2)使用计数的哈希表
    void usingCounting(string& str) {
        vector<int> arr(26, 0);

        for (char c : str) arr[c - 'a']++;

        string temp;
        for (int num : arr) temp.push_back(num + '0');
        if (m.count(temp)) m[temp].push_back(str);
        else m.insert(pair<string, vector<string>>(temp, { str }));
    }

    // (3)使用质数相乘的哈希表
    void usingPrimes(string& str) {
        unsigned long sum = 1; // long 和 int 类型会overflow
        for (char c : str) sum *= primes[c - 'a'];
        if (m1.count(sum)) m1[sum].push_back(str);
        else m1.insert(pair<unsigned long, vector<string>>(sum, { str }));
    }

public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        for (string& str : strs) {
            usingSort(str);
            //usingCounting(str);
            //usingPrimes(str);
        }

        for (auto str : m) ans.push_back(str.second);
        //for (auto str : m1) ans.push_back(str.second); // 仅(3)使用

        return ans;
    }
};

5. 参考资料

  1. 49. Group Anagrams
  2. 49. Group Anagrams

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值