《LeetCode之每日一题》:94.面试题 10.02. 变位词组
Posted 是七喜呀!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《LeetCode之每日一题》:94.面试题 10.02. 变位词组相关的知识,希望对你有一定的参考价值。
题目链接: 面试题 10.02. 变位词组
有关题目
编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。
变位词是指字母相同,但排列不同的字符串。
注意:本题相对原题稍作修改
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
题解
方法一:排序
思路:
互为变位词的两个字符串包含的字母相同
因此对两个字符串分别进行排序之后得到的字符串一定是相同的
故可以将排序之后的字符串作为哈希表的键
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for (string& str: strs)
{
string key = str;
sort(key.begin(), key.end());
mp[key].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
//for循环改为for(auto& [_, vs]: m) ans.emplace_back(move(vs));
//也可以
return ans;
}
};
方法二:计数
互为变位词的两个字符串包含的字母相同
两个字符串中的相同字母出现的次数一定是相同
故使用计数数组作为哈希表的键
对于可能发生hash碰撞,需要处理碰撞
代码一:
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
//
Map<String, List<String>> map = new HashMap<String, List<String>>();
//List集合初始化 List list = new ArrayList();类比vector容器
for (String str : strs)
{
int[] counts = new int[26];
int length = str.length();
for (int i = 0; i < length; i++)
{
counts[str.charAt(i) - 'a']++;//数组记录每个字母出现的次数
}
// 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 26; i++)
{
if (counts[i] != 0)
{
sb.append((char) ('a' + i));
sb.append(counts[i]);
}
}
String key = sb.toString();
List<String> list = map.getOrDefault(key, new ArrayList<String>());
//哈希中getOrDefault存在Object key,此处则返回key
//如果不存在Object key,就返回默认值 V defaultValue,此处则返回new ArrayList<String>()
list.add(str);
map.put(key, list);
}
return new ArrayList<List<String>>(map.values());
}
}
代码二:哈希值处理哈希之b进制
参考梦璃夜·天星
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<uint64_t, vector<string> > m;
uint64_t pf[26] = {1}, b = 97755331;
for(int i = 1; i < 26; ++i) pf[i] = pf[i-1] * b;//下标i[0,25],对应数组元素为b^i
for(auto& t: strs){
uint64_t hash = 0;
//构建一个法则保证出现不同字符的字符串
//能被hash表不重复记录
for(char c: t) hash += c * pf[c - 'a'];
m[hash].push_back(t);
}
vector<vector<string>> ans;
for(auto& [_, vs]: m) ans.emplace_back(move(vs));
return ans;
}
};
代码三:哈希值处理哈希之素数积
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<uint64_t, vector<string> > m;
const uint64_t pf[26] = {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};
for(auto& t: strs){
uint64_t hash = 1;//通过typedef定义的无符号long long int(64)
//构建一个法则保证出现不同字符的字符串
//能被hash表不重复记录
for(char c: t) hash *= pf[c - 'a'];
m[hash].push_back(t);
}
vector<vector<string>> ans;
for(auto& [_, vs]: m) ans.emplace_back(move(vs));
return ans;
}
};
代码四:构建哈希函数
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
// 自定义对 array<int, 26> 类型的哈希函数
//哈希函数 大致上就是用了lambda表达式和accumulate求和函数(再嵌套lambda),实现把26位array转成一个数字作为key
//accumulate第四个参数为自定义数据类型
//我们需要自己动手写一个回调函数来实现自定义数据
//这里第四个参数为lambda表达式,返回值为(acc << 1) ^ fn(num)
auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
return (acc << 1) ^ fn(num);
});
};
unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
//decltype就是一种类型说明符,不实际计算表达式的值,编译器分析表达式并得到它的类型
//arrayHash指的上文自定义类型的哈希函数
for (string& str: strs) {
array<int, 26> counts{};
int length = str.length();
for (int i = 0; i < length; ++i) {
counts[str[i] - 'a'] ++;
}
mp[counts].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
return ans;
}
};
accumulate第四个参数示例:
#include<iostream>
#include <vector>
#include <string>
#include<numeric>
using namespace std;
struct Grade
{
string name;
int grade;
};
int main()
{
Grade subject[3] = {
{ "English", 80 },
{ "Biology", 70 },
{ "History", 90 }
};
int sum = accumulate(subject, subject + 3, 0, [](int a, Grade b){return a + b.grade; });
cout << sum << endl;
system("pause");
return 0;
}
以上是关于《LeetCode之每日一题》:94.面试题 10.02. 变位词组的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode每日一题2020.6.26 面试题 02.01. 移除重复节点