题目描述
给你一个字符串数组 words
,请你找出所有在 words
的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
示例 1:
输入:words = ["bella","label","roller"] 输出:["e","l","l"]
示例 2:
输入:words = ["cool","lock","cook"] 输出:["c","o"]
提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i]
由小写英文字母组成
NOTE:由 words[i]
由小写英文字母组成 ,返回的是公共字符,可以想到使用哈希法
卡哥原话:这道题目一眼看上去,就是用哈希法,“小写字符”,“出现频率”, 这些关键字都是为哈希法量身定做的啊
思路:这道题说要返回字符串中的所有公共字符,并且都是由小写英文字母构成,就可以想到使用数组按照26个小写字母相对于‘a’的ascii码的索引数组,数组中存放的是字符的出现次数,整体思路就是统计出搜索字符串里26个字符的出现的频率,然后取每个字符频率最小值,最后转成输出格式就可以了
先统计第一个字符串所有字符出现的次数
int hash[26] = {0};// 用来统计所有字符串里字符出现的最小频率
for(int i=0 ;i<words[0].size() ;i++) { // 用第一个字符串给hash初始化
hash[words[0][i] - 'a']++;
}
假设我们有三个字符串作为输入,如下:
["bella", "label", "roller"]
- 首先,用第一个字符串 "bella" 初始化
hash
数组,记录每个字符的频率。hash['b' - 'a'] = 1
hash['e' - 'a'] = 1
hash['l' - 'a'] = 2
hash['a' - 'a'] = 1
接下来,把其他字符串里字符的出现次数也统计出来一次放在hashOtherStr中。
然后hash 和 hashOtherStr 取最小值,这是本题关键所在,此时取最小值,就是 一个字符在所有字符串里出现的最小次数了。
for(int i =1; i <words.size() ;i++ ) { // 统计除第一个字符串外字符的出现频率
memset(hashOtherStr , 0 ,26*sizeof(int));
for(int j = 0;j<words[i].size() ;j++) {
hashOtherStr[words[i][j] - 'a']++;
}
// 这是关键所在
for(int k =0; k < 26; k++) {
// 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数
hash[k] = min(hash[k] , hashOtherStr[k]);
}
}
-
处理第二个字符串:"label"
- 重置
hashOtherStr
并统计 "label" 中每个字符的频率。hashOtherStr['l' - 'a'] = 2
hashOtherStr['a' - 'a'] = 1
hashOtherStr['b' - 'a'] = 1
hashOtherStr['e' - 'a'] = 1
- 更新
hash
数组以存储每个字符的最小频率:hash['l' - 'a'] = min(2, 2) = 2
hash['a' - 'a'] = min(1, 1) = 1
hash['b' - 'a'] = min(1, 1) = 1
hash['e' - 'a'] = min(1, 1) = 1
- 重置
-
处理第三个字符串:"roller"
- 重置
hashOtherStr
并统计 "roller" 中每个字符的频率。hashOtherStr['r' - 'a'] = 2
hashOtherStr['o' - 'a'] = 1
hashOtherStr['l' - 'a'] = 2
hashOtherStr['e' - 'a'] = 1
- 更新
hash
数组以存储每个字符的最小频率:hash['l' - 'a'] = min(2, 2) = 2
("l" 在所有字符串中都至少出现2次)hash['e' - 'a'] = min(1, 1) = 1
("e" 在所有字符串中都至少出现1次)
- 重置
此时hash里统计着字符在所有字符串里出现的最小次数,那么把hash转成题目要求的输出格式就可以了。
// 将hash统计的字符次数,转成输出形式
for (int i = 0; i < 26; i++) {
while (hash[i] != 0) { // 注意这里是while,多个重复的字符
string s(1, i + 'a'); // char -> string
result.push_back(s);
hash[i]--;
}
}
- 最终,从
hash
数组中读取每个字符的统计数据,并将其转换为字符串格式输出。 - 结果:
["e", "l", "l"]
-
外层循环 (
for
):遍历从0到25的每个索引i
,这些索引代表了字符从 'a' 到 'z'。 -
内层循环 (
while
):对于每个字符,只要hash[i]
的值不为0,就进入循环体。-
string s(1, i + 'a');
:这行代码创建一个字符串s
,包含单个字符i + 'a'
。例如,如果i = 0
,那么i + 'a'
就是'a'
,所以s
就是"a"
。string
类的构造函数有多种重载形式,其中一种允许创建一个具有特定长度和特定字符的字符串。这行代码string s(1, i + 'a');
利用了这种构造函数。 -
result.push_back(s);
:将字符串s
添加到结果向量result
中。 -
hash[i]--;
:将hash[i]
减1,表示已经将一个实例的字符添加到结果中。
-
-
重复添加:如果
hash[i]
的值是2,while
循环会执行两次,每次都添加相同的字符i + 'a'
到result
中,因此,相同的字符会被添加两次。比如,如果i
对应于字符'a'
,且hash[i]
值为2,则最终result
中将包含两个"a"
。
完整代码
class Solution {
public:
vector<string> commonChars(vector<string>& words) {
vector<string> result;
if(words.size() == 0) {
return result;
}
int hash[26] = {0};
for(int i=0 ;i<words[0].size() ;i++) {
hash[words[0][i] - 'a']++;
}
int hashOtherStr[26] = {0};
for(int i =1; i <words.size() ;i++ ) {
memset(hashOtherStr , 0 ,26*sizeof(int));
for(int j = 0;j<words[i].size() ;j++) {
hashOtherStr[words[i][j] - 'a']++;
}
for(int k =0; k < 26; k++) {
hash[k] = min(hash[k] , hashOtherStr[k]);
}
}
for(int i = 0; i < 26 ;i++) {
while(hash[i] != 0) {
string s(1,i+'a');
result.push_back(s);
hash[i]--;
}
}
return result;
}
};