算法刷题-哈希表-查找常用字符

news/2025/1/8 20:42:54/

查找常用字符

  • 1002. 查找常用字符
  • 思路
    • 其他语言版本

1002. 查找常用字符

力扣题目链接

给你一个字符串数组 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] 由小写英文字母组成

思路

这道题意一起就有点绕,不是那么容易懂,其实就是26个小写字符中有字符 在所有字符串里都出现的话,就输出,重复的也算。

例如:

输入:[“ll”,“ll”,“ll”]
输出:[“l”,“l”]

这道题目一眼看上去,就是用哈希法,“小写字符”,“出现频率”, 这些关键字都是为哈希法量身定做的啊

首先可以想到的是暴力解法,一个字符串一个字符串去搜,时间复杂度是 O ( n m ) O(n^m) O(nm),n是字符串长度,m是有几个字符串。

可以看出这是指数级别的时间复杂度,非常高,而且代码实现也不容易,因为要统计 重复的字符,还要适当的替换或者去重。

了解了哈希法,理解了数组在哈希法中的应用之后,可以来看解题思路了。

整体思路就是统计出搜索字符串里26个字符的出现的频率,然后取每个字符频率最小值,最后转成输出格式就可以了。

如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-izLvrgYT-1686463484261)(https://code-thinking.cdn.bcebos.com/pics/1002.查找常用字符.png)]

先统计第一个字符串所有字符出现的次数,代码如下:

int hash[26] = {0}; // 用来统计所有字符串里字符出现的最小频率
for (int i = 0; i < A[0].size(); i++) { // 用第一个字符串给hash初始化hash[A[0][i] - 'a']++;
}

接下来,把其他字符串里字符的出现次数也统计出来一次放在hashOtherStr中。

然后hash 和 hashOtherStr 取最小值,这是本题关键所在,此时取最小值,就是 一个字符在所有字符串里出现的最小次数了。

代码如下:

int hashOtherStr[26] = {0}; // 统计除第一个字符串外字符的出现频率
for (int i = 1; i < A.size(); i++) {memset(hashOtherStr, 0, 26 * sizeof(int));for (int j = 0; j < A[i].size(); j++) {hashOtherStr[A[i][j] - 'a']++;}// 这是关键所在for (int k = 0; k < 26; k++) { // 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数hash[k] = min(hash[k], hashOtherStr[k]);}
}

此时hash里统计着字符在所有字符串里出现的最小次数,那么把hash转成题目要求的输出格式就可以了。

代码如下:

// 将hash统计的字符次数,转成输出形式
for (int i = 0; i < 26; i++) {while (hash[i] != 0) { // 注意这里是while,多个重复的字符string s(1, i + 'a'); // char -> stringresult.push_back(s);hash[i]--;}
}

整体C++代码如下:

class Solution {
public:vector<string> commonChars(vector<string>& A) {vector<string> result;if (A.size() == 0) return result;int hash[26] = {0}; // 用来统计所有字符串里字符出现的最小频率for (int i = 0; i < A[0].size(); i++) { // 用第一个字符串给hash初始化hash[A[0][i] - 'a']++;}int hashOtherStr[26] = {0}; // 统计除第一个字符串外字符的出现频率for (int i = 1; i < A.size(); i++) {memset(hashOtherStr, 0, 26 * sizeof(int));for (int j = 0; j < A[i].size(); j++) {hashOtherStr[A[i][j] - 'a']++;}// 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数for (int k = 0; k < 26; k++) {hash[k] = min(hash[k], hashOtherStr[k]);}}// 将hash统计的字符次数,转成输出形式for (int i = 0; i < 26; i++) {while (hash[i] != 0) { // 注意这里是while,多个重复的字符string s(1, i + 'a'); // char -> stringresult.push_back(s);hash[i]--;}}return result;}
};

其他语言版本

Java:

class Solution {public List<String> commonChars(String[] A) {List<String> result = new ArrayList<>();if (A.length == 0) return result;int[] hash= new int[26]; // 用来统计所有字符串里字符出现的最小频率for (int i = 0; i < A[0].length(); i++) { // 用第一个字符串给hash初始化hash[A[0].charAt(i)- 'a']++;}// 统计除第一个字符串外字符的出现频率for (int i = 1; i < A.length; i++) {int[] hashOtherStr= new int[26];for (int j = 0; j < A[i].length(); j++) {hashOtherStr[A[i].charAt(j)- 'a']++;}// 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数for (int k = 0; k < 26; k++) {hash[k] = Math.min(hash[k], hashOtherStr[k]);}}// 将hash统计的字符次数,转成输出形式for (int i = 0; i < 26; i++) {while (hash[i] != 0) { // 注意这里是while,多个重复的字符char c= (char) (i+'a');result.add(String.valueOf(c));hash[i]--;}}return result;}
}

Python

class Solution:def commonChars(self, words: List[str]) -> List[str]:if not words: return []result = []hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率for i, c in enumerate(words[0]):  # 用第一个字符串给hash初始化hash[ord(c) - ord('a')] += 1# 统计除第一个字符串外字符的出现频率for i in range(1, len(words)):hashOtherStr = [0] * 26for j in range(len(words[i])):hashOtherStr[ord(words[i][j]) - ord('a')] += 1# 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数for k in range(26):hash[k] = min(hash[k], hashOtherStr[k])# 将hash统计的字符次数,转成输出形式for i in range(26):while hash[i] != 0: # 注意这里是while,多个重复的字符result.extend(chr(i + ord('a')))hash[i] -= 1return result

Python 3 使用collections.Counter

class Solution:def commonChars(self, words: List[str]) -> List[str]:tmp = collections.Counter(words[0])l = []for i in range(1,len(words)):# 使用 & 取交集tmp = tmp & collections.Counter(words[i])# 剩下的就是每个单词都出现的字符(键),个数(值)for j in tmp:v = tmp[j]while(v):l.append(j)v -= 1return l

javaScript

var commonChars = function (words) {let res = []let size = 26let firstHash = new Array(size).fill(0)   // 初始化 hash 数组let a = "a".charCodeAt()let firstWord = words[0]for (let i = 0; i < firstWord.length; i++) { // 第 0 个单词的统计let idx = firstWord[i].charCodeAt()firstHash[idx - a] += 1}let otherHash = new Array(size).fill(0)    // 初始化 hash 数组for (let i = 1; i < words.length; i++) { // 1-n 个单词统计for (let j = 0; j < words[i].length; j++) {let idx = words[i][j].charCodeAt()otherHash[idx - a] += 1}for (let i = 0; i < size; i++) {firstHash[i] = Math.min(firstHash[i], otherHash[i])}otherHash.fill(0)}for (let i = 0; i < size; i++) {while (firstHash[i] > 0) {res.push(String.fromCharCode(i + a))firstHash[i]--}}return res
};

TypeScript

  console.time("test")let str: string = ""//设置一个用字母组成的map字典let map = new Map<string, number>()//给所有map设置初始值为0let wordInitial: [string, number][] = words[0].split("").map((item) => [item, 0])//如果有重复字母,就把重复字母的数量加1for (let word of words[0]) {map.set(word, map.has(word) ? map.get(word) + 1 : 1)}for (let i = 1; i < words.length; i++) {const mapWord = new Map<string, number>(wordInitial)for (let j = 0; j < words[i].length; j++) {if (!map.has(words[i][j])) continue//mapWord中的字母的个数不能高于当前map的个数,多于则不能添加if (map.get(words[i][j]) > mapWord.get(words[i][j])) {mapWord.set(words[i][j],mapWord.has(words[i][j]) ? mapWord!.get(words[i][j]) + 1 : 1)}}//每次重新初始化mapmap = mapWord}for (let [key, value] of map) {str += key.repeat(value)}console.timeEnd("test")return str.split("")

GO

func commonChars(words []string) []string {length:=len(words)fre:=make([][]int,0)//统计每个字符串的词频res:=make([]string,0)//统计词频for i:=0;i<length;i++{var row [26]int//存放该字符串的词频for j:=0;j<len(words[i]);j++{row[words[i][j]-97]++}fre=append(fre,row[:])}//查找一列的最小值for j:=0;j<len(fre[0]);j++{pre:=fre[0][j]for i:=0;i<len(fre);i++{pre=min(pre,fre[i][j])}//将该字符添加到结果集(按照次数)tmpString:=string(j+97)for i:=0;i<pre;i++{res=append(res,tmpString)}}return res
}
func min(a,b int)int{if a>b{return b}return a
}

Swift:

func commonChars(_ words: [String]) -> [String] {var res = [String]()if words.count < 1 {return res}let aUnicodeScalarValue = "a".unicodeScalars.first!.valuelet lettersMaxCount = 26// 用于统计所有字符串每个字母出现的 最小 频率var hash = Array(repeating: 0, count: lettersMaxCount)// 统计第一个字符串每个字母出现的次数for unicodeScalar in words.first!.unicodeScalars {hash[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1}// 统计除第一个字符串每个字母出现的次数for idx in 1 ..< words.count {var hashOtherStr = Array(repeating: 0, count: lettersMaxCount)for unicodeScalar in words[idx].unicodeScalars {hashOtherStr[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1}// 更新hash,保证hash里统计的字母为出现的最小频率for k in 0 ..< lettersMaxCount {hash[k] = min(hash[k], hashOtherStr[k])}}// 将hash统计的字符次数,转成输出形式for i in 0 ..< lettersMaxCount {while hash[i] != 0 { // 注意这里是while,多个重复的字符let currentUnicodeScalarValue: UInt32 = UInt32(i) + aUnicodeScalarValuelet currentUnicodeScalar: UnicodeScalar = UnicodeScalar(currentUnicodeScalarValue)!let outputStr = String(currentUnicodeScalar) // UnicodeScalar -> Stringres.append(outputStr)hash[i] -= 1}}return res
}

C:

//若两个哈希表定义为char数组(每个单词的最大长度不会超过100,因此可以用char表示),可以提高时间和空间效率
void updateHashTable(int* hashTableOne, int* hashTableTwo) {int i;for(i = 0; i < 26; i++) {hashTableOne[i] = hashTableOne[i] < hashTableTwo[i] ? hashTableOne[i] : hashTableTwo[i];}
}char ** commonChars(char ** words, int wordsSize, int* returnSize){//用来统计所有字母出现的最小频率int hashTable[26] = { 0 };//初始化返回的char**数组以及返回数组长度*returnSize = 0;char** ret = (char**)malloc(sizeof(char*) * 100);//如果输入数组长度为0,则返回NULLif(!wordsSize) return NULL;int i;//更新第一个单词的字母频率for(i = 0; i < strlen(words[0]); i++)hashTable[words[0][i] - 'a']++;//更新从第二个单词开始的字母频率for(i = 1; i < wordsSize; i++) {//创建新的哈希表,记录新的单词的字母频率int newHashTable[26] = { 0 };int j;for(j = 0; j < strlen(words[i]); j++) {newHashTable[words[i][j] - 'a']++;}//更新原哈希表updateHashTable(hashTable, newHashTable);}//将哈希表中的字符变为字符串放入ret中for(i = 0; i < 26; i++) {if(hashTable[i]) {int j;for(j = 0; j < hashTable[i]; j++) {char* tempString = (char*)malloc(sizeof(char) * 2);tempString[0] = i + 'a';tempString[1] = '\0';ret[(*returnSize)++] = tempString;}}}return ret;
}

Scala:

object Solution {def commonChars(words: Array[String]): List[String] = {// 声明返回结果的不可变List集合,因为res要重新赋值,所以声明为varvar res = List[String]()var hash = new Array[Int](26) // 统计字符出现的最小频率// 统计第一个字符串中字符出现的次数for (i <- 0 until words(0).length) {hash(words(0)(i) - 'a') += 1}// 统计其他字符串出现的频率for (i <- 1 until words.length) {// 统计其他字符出现的频率var hashOtherStr = new Array[Int](26)for (j <- 0 until words(i).length) {hashOtherStr(words(i)(j) - 'a') += 1}// 更新hash,取26个字母最小出现的频率for (k <- 0 until 26) {hash(k) = math.min(hash(k), hashOtherStr(k))}}// 根据hash的结果转换输出的形式for (i <- 0 until 26) {for (j <- 0 until hash(i)) {res = res :+ (i + 'a').toChar.toString}}res}
}

Rust:

impl Solution {pub fn common_chars(words: Vec<String>) -> Vec<String> {if words.is_empty() {return vec![];}let mut res = vec![];let mut hash = vec![0; 26];for i in words[0].bytes() {hash[(i - b'a') as usize] += 1;}for i in words.iter().skip(1) {let mut other_hash_str = vec![0; 26];for j in i.bytes() {other_hash_str[(j - b'a') as usize] += 1;}for k in 0..26 {hash[k] = hash[k].min(other_hash_str[k]);}}for (i, v) in hash.iter_mut().enumerate() {while *v > 0 {res.push(((i as u8 + b'a') as char).to_string());*v -= 1;}}res}
}

http://www.ppmy.cn/news/327586.html

相关文章

使用apt处理注解-将观察者模式用于apt

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** * 使用apt处理注解* * 注解处理工具apt&#xff0c;这是Sun为了帮助注解的处理过程而提供的工具…

关于macbook只能登陆微信但无法打开网络的问题解决

遇到的问题&#xff1a; macbook在连接网络之后&#xff0c;只能登陆微信和邮箱等第三方应用软件&#xff0c;但是无法在浏览器中打开任何网页。尝试过&#xff1a;重启、更换网络连接方式、浏览器类型之后仍然无法解决。 网上找到的方法&#xff1a; 修改DNS&#xff08;http…

signal

读信号&#xff0c;dqs 是对齐到dq的边沿&#xff0c; 写信号&#xff0c;dqs 的边沿是对到中间的。 spec 就是这样规定的。我们在dq的最中间的采样&#xff0c;肯定是最安全的。 dqs 是对齐到dq的边沿 &#xff0c; 在silicon 内部&#xff0c;还是通过移位完成的。 rl: re…

草原风光

前几天出了趟差&#xff0c;在桑根达来转车的时候在附近拍了几张照片&#xff0c;把还过得去的拿出来秀一下 http://lvjack163.blog.163.com/album

卑微小测试的一天----自动生成正交法测试用例

前言 工作过程中&#xff0c;我们接触到需求后第一要务是 熟悉需求并且输出测试用例&#xff0c;针对接口测试的入参测试&#xff0c;需要校验大量入参的组合场景&#xff0c;这时我们通常采用正交法来设计测试用例&#xff0c;在减少测试用例的数量时&#xff0c;同时保障测试…

算法学习day17

110 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true…

Android三大按钮,模拟Android导航栏三大金刚按键点击

模拟Android导航栏三大金刚按键点击 这里需要使用的是AccessibilityService无障碍辅助服务&#xff0c;可以全局监听界面所有的变化&#xff1b; 1.构建无障碍服务 public class FloatService extends AccessibilityService { private static AccessibilityService service; pu…

变形金刚2 昨日上映 汽车人提前降临地球

万众瞩目耗资约13.6亿人民币的《变形金刚2》终于在昨天全球上映了&#xff0c;这部电影的上映不仅让全球影迷大呼过瘾&#xff0c;同时也令全球的车迷大饱眼福。随着一句“汽车人&#xff0c;变形出发”《变形金刚2》一定会引爆整个暑期档。 汽车人已经降临地球 明天汽车人就将…