# [49. Group Anagrams](https://leetcode.com/problems/group-anagrams/) # 思路 ## 思路一 题目要求找出字符串数组里的anagram(同字母异序词),所有的字母都是小写字母。 如何判断两个字符串是否是anagram呢,有两种方法: 1. 先排序再判断排序后的字符串是否相等; 2. 遍历一遍,然后用一个数组记录26个字母每个字母出现的次数,再比较这个次数(类似桶排序)。 如果方法1使用桶排序的话其实两种方法就是差不多的了。 解决了如何判断的问题,算法思路就清晰了: 对strs中的每个字符串的副本str,对其进行排序(可以使用桶排序也可使用现成的sort),然后用一个hash表mp来记录这个str是否在以前出现过。 若`mp[str] == 0`则说明没有出现过;否则则出现过,mp[str]的值代表最终返回结果数组(即代码中的res)中对应元素的下标加一。 若strs中有n个字符串,每个字符串长度平均为m,则桶排序时间复杂度为O(m),如果使用的是unordered_map(对应hash)则查找复杂度O(1),所以总体时间复杂度O(mn)。 注意: unordered_map(对应hash)比map(对应红黑树)快一些,所以使用map的时候如果追求时间复杂度则一律使用unordered_map。 ## 思路二 讨论区还有一种比较tricky的方法:思路一我们先进行(桶)排序的目的是方便后续判断【26个字母出现次数】是否完全一样,如果一样就是anagram。我们也可以将26个字母用26个不同的素数代替,然后将字符串中所有字母对应的素数乘起来,如果两个字符串最后得到的乘积相等,那么是anagram。 注意这个乘积可能很大,亲测long long还会溢出,unsigned long long才不会溢出。所以说此种方法仅供开阔思路。 时间复杂度同思路一 # C++ ## 思路一 ``` C++ class Solution { private: string count_sort(const string &str){ // 桶排序,返回str排序后的副本 int count[26] = {0}; for(char c: str) count[c - 'a']++; string res; for(int i = 0; i < 26; i++) res.append(count[i], i + 'a'); // 在res后面加上count[i]个字符 i + 'a' return res; } public: vector> groupAnagrams(vector& strs) { vector>res; unordered_mapmp; int n = strs.size(); for(int i = 0; i < n; i++){ string str = count_sort(strs[i]); // 使用桶排序理论上会快一些 // string str = strs[i]; // sort(str.begin(), str.end()); int tmp = mp[str]; if(tmp == 0) { // 没出现过 res.push_back(vector(1, strs[i])); mp[str] = res.size(); // map[str]的值代表res对应元素的下标加一 } else res[tmp - 1].push_back(strs[i]); } return res; } }; ``` ## 思路二 ``` C++ class Solution { public: vector primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101}; vector> groupAnagrams(vector& strs) { vector>res; unordered_mapmp; for(string s: strs){ unsigned long long hash = 1; for(char c: s) hash *= primes[c - 'a']; if(!mp.count(hash)){ res.push_back({s}); mp[hash] = res.size(); } else res[mp[hash] - 1].push_back(s); } return res; } }; ```