Prompt
Given an array of strings strs, group the together. You can return the answer in any order.
Constraints:
1 <= strs.length <= 1040 <= strs[i].length <= 100strs[i]consists of lowercase English letters.
Examples
- Example 1:
- Input: strs = [“eat”,“tea”,“tan”,“ate”,“nat”,“bat”]
- Output: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]
- Explanation:
- There is no string in strs that can be rearranged to form
"bat". - The strings
"nat"and"tan"are anagrams as they can be rearranged to form each other. - The strings
"ate","eat", and"tea"are anagrams as they can be rearranged to form each other.
- There is no string in strs that can be rearranged to form
- Example 2:
- Input: strs = [""]
- Output: ""
- Example 3:
- Input: strs = [“a”]
- Output: “a”
Solutions
Sorted String Keyed HashMap Solution
In C++
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>>
sorted_word_to_anagrams;
for (const auto& str : strs) {
string sorted_str(str);
sort(sorted_str.begin(), sorted_str.end());
sorted_word_to_anagrams[sorted_str].push_back(str);
}
vector<vector<string>> result;
for (const auto& pair : sorted_word_to_anagrams) {
result.push_back(move(pair.second));
}
return result;
}Explanation
We use sorting as a perfect Hash Function for anagrams that is guaranteed to have no collisions. The price of no collisions ever is worse time complexity. We have a HashMap between the sorted strings and lists of anagrams. After we finish the map, we extract all the KV-values, and return the resulting list of lists. Note that we make sure to avoiding copying by moving each list into the final list.
Big O Analysis
- Let be the number of strings in the input array
- Let be the maximum string length
Time Complexity
The time complexity is because our keys take to create, and we construct keys.
Auxiliary Space Complexity
The space complexity is .
Boost Hash Combine Custom Frequency Map Keyed HashMap Solution
In C++
struct Key {
int freq[26] = {};
Key(string str) {
for (char c : str) ++freq[c - 'a'];
}
bool operator==(const Key& other) const {
return memcmp(this->freq, other.freq, 26 * sizeof(int)) == 0;
}
};
namespace std {
template <>
struct hash<Key> {
size_t operator()(const Key& k) const {
size_t h = 0;
for (int i = 0; i < 26; i++) {
h ^= hash<int>{}(k.freq[i]) + 0x9e3779b9 + (h << 6) + (h >> 2);
}
return h;
}
};
}
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<Key, vector<string>> strhash_to_anagrams;
for (const auto& str : strs) {
strhash_to_anagrams[Key(str)].push_back(str);
}
vector<vector<string>> result;
for (const auto& pair : strhash_to_anagrams) {
result.push_back(std::move(pair.second));
}
return result;
}Explanation
This solution is the same as the previous but we use better keys than just the sorted string; instead we use custom keys that contain a static array frequency map and use a better Hash Function called Boost Hash Combine.
Big O Analysis
- Let be the number of strings in the input array
- Let be the maximum string length
Time Complexity
The time complexity is because our keys only take to construct.
Auxiliary Space Complexity
The space complexity is .