《LeetCode之每日一题》:94.面试题 10.02. 变位词组

面试题 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;
    }
};

代码二:空间极致优化的排序哈希

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string_view, int> m;
        vector<vector<string>> ans;
        int dif = 0;
        for(auto& t: strs){
            string s = t;
            sort(begin(t), end(t));
            auto [p, f] = m.insert({t, dif});
            //map::insert 返回一个pair、
            //第一项是指向插入后的元素的迭代器,第二项是是否成功插入(如果原先就有,不会插入新的值)
            //同时标记p为dif
            if(f){
                ++dif;
                ans.emplace_back();
            }
            ans[p->second].emplace_back(move(s));
           //auto[ , ] c17语法 p,f对应pair两项
        }
        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;
}

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值