0.哈希表想法的延续
学习代码随想录的时候看到了【字母异位词】这个概念。代码随想录 (programmercarl.com)
简单说一下针对【字母异位词】从定义出发的代码表示。统计一个单词每个字母的出现次数,最多只有26个字母,所以使用26个「坑」来记录每个字母的出现次数,如果两个单词的每个字母出现次数都一致,那么这两个单词就是【字母异位词】。
针对代码随想录给出的这道例题,采用了一种很巧妙的方式来【比较】,因为是两个单词比较,所以第一遍统计采用【加法】,第二遍统计采用【减法】,如果两个单词是【字母异位词】,那么会在每一个字母上【加减相抵】最后就是全0。
对于第49题,就需要用一点小巧思。
1.哈希在何处
哈希需要有key和value,根据第0部分给出的【字母异位词】定义的表示,我们可以发现,如果一些单词是字母异位词,那么它们的字母计数「坑」长得应该一模一样。而用什么样的表示可以体现出一模一样呢?
如果和代码随想录例题一样,采取数组的方式,作为key显然是不合适的。就算不考虑key这一层,要比较两个数组是否一致,就需要遍历一遍数组,如果有n个待判断的字符串,那时间复杂度就达到了O(n^2)——欢迎指正,如果这里的时间复杂度分析错了的话。
这时候,有一个神奇的数据结构——【字符串】出现了。它可以使用类似数组的方式访问,同时字符串的相等很好判断,此外字符串作为key并没有什么让人觉得奇怪的地方。所以我们使用【字符串】来统计每个单词的字符出现次数。
那接下来就是value是什么了。这里就比较水到渠成了,刚刚有cue到——”如果一些单词是字母异位词,那么它们的字母计数「坑」长得应该一模一样“。从哈希的角度来说,就是经过我的【统计函数】,【字母异位词】会被映射到同一个key(位置)下。而这就是我们想要的。于是,使用vector来存储【字母异位词】列表(就很像解决哈希冲突的拉链法哈哈哈)。
2.中间数据结构的设计
单独拎出来表示一下上一部分说的哈希表的表示。key是统计字母的字符串,value是单词列表。用C++声明就是:
unordered_map<string,vector<string>>
3.代码实现
class Solution {
public:
string countLetter(const string& s){
string count(26,'0');//生成一个26位全0字符串
for(auto c:s){
count[c-'a']++;
}
return count;//返回统计每个字母出现次数的字符串,作为key
}
vector<vector<string>> groupAnagrams(vector<string>& strs){
//对每一个单词建立一个字母统计数组,统计结果一样的分一组——统计结果作为key
unordered_map<string,vector<string>> res;//key为字母统计结果,value为相同字母统计结果的string列表
for(string str:strs){
string count=countLetter(str);
res[count].push_back(str);//当前str入相同计数的队列
}
// 转换格式
vector<vector<string>> finalRes;
for(auto it=res.begin();it!=res.end();it++){
finalRes.push_back(it->second);
}
return finalRes;
}
};
4.写在最后
在周末被这道题卡住之后,主打一个一蹶不振。今天在GPT大人的帮助下,终于理解了这个解法的idea了。官方题解的【排序】也是我完全没想到的切入点。
哎,道阻且长。
最后一个小tips送给自己:
在函数string countLetter(const string& s)中,参数要声明为const类型,因为传入的是引用,而我们只要获得它的值就可以了,为了防止修改引用的变量值,需要声明为const类型。
哈希真是好东西,明天继续冲算法!!加油!!!!
Energy and persistent conquer all things.
一定拿下,全都拿下。㊗️大家金三银四顺利。
于 二月二,龙抬头。