参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

> 在哈希法中有一些场景就是为数组量身定做的。 # 383. 赎金信 [力扣题目链接](https://leetcode.cn/problems/ransom-note/) 给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。 (题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。) **注意:** 你可以假设两个字符串均只含有小写字母。 canConstruct("a", "b") -> false canConstruct("aa", "ab") -> false canConstruct("aa", "aab") -> true ## 思路 这道题目和[242.有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html)很像,[242.有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html)相当于求 字符串a 和 字符串b 是否可以相互组成 ,而这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。 本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。 *  第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思”  这里*说明杂志里面的字母不可重复使用。* * 第二点 “你可以假设两个字符串均只含有小写字母。” *说明只有小写字母*,这一点很重要 ### 暴力解法 那么第一个思路其实就是暴力枚举了,两层for循环,不断去寻找,代码如下: ```CPP class Solution { public: bool canConstruct(string ransomNote, string magazine) { for (int i = 0; i < magazine.length(); i++) { for (int j = 0; j < ransomNote.length(); j++) { // 在ransomNote中找到和magazine相同的字符 if (magazine[i] == ransomNote[j]) { ransomNote.erase(ransomNote.begin() + j); // ransomNote删除这个字符 break; } } } // 如果ransomNote为空,则说明magazine的字符可以组成ransomNote if (ransomNote.length() == 0) { return true; } return false; } }; ``` * 时间复杂度: O(n^2) * 空间复杂度: O(1) 这里时间复杂度是比较高的,而且里面还有一个字符串删除也就是erase的操作,也是费时的,当然这段代码也可以过这道题。 ### 哈希解法 因为题目说只有小写字母,那可以采用空间换取时间的哈希策略,用一个长度为26的数组来记录magazine里字母出现的次数。 然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。 依然是数组在哈希法中的应用。 一些同学可能想,用数组干啥,都用map完事了,**其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!** 代码如下: ```CPP class Solution { public: bool canConstruct(string ransomNote, string magazine) { int record[26] = {0}; //add if (ransomNote.size() > magazine.size()) { return false; } for (int i = 0; i < magazine.length(); i++) { // 通过record数据记录 magazine里各个字符出现次数 record[magazine[i]-'a'] ++; } for (int j = 0; j < ransomNote.length(); j++) { // 遍历ransomNote,在record里对应的字符个数做--操作 record[ransomNote[j]-'a']--; // 如果小于零说明ransomNote里出现的字符,magazine没有 if(record[ransomNote[j]-'a'] < 0) { return false; } } return true; } }; ``` * 时间复杂度: O(n) * 空间复杂度: O(1) ## 其他语言版本 ### Java: ```Java class Solution { public boolean canConstruct(String ransomNote, String magazine) { // shortcut if (ransomNote.length() > magazine.length()) { return false; } // 定义一个哈希映射数组 int[] record = new int[26]; // 遍历 for(char c : magazine.toCharArray()){ record[c - 'a'] += 1; } for(char c : ransomNote.toCharArray()){ record[c - 'a'] -= 1; } // 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符 for(int i : record){ if(i < 0){ return false; } } return true; } } ``` ### Python: (版本一)使用数组 ```python class Solution: def canConstruct(self, ransomNote: str, magazine: str) -> bool: ransom_count = [0] * 26 magazine_count = [0] * 26 for c in ransomNote: ransom_count[ord(c) - ord('a')] += 1 for c in magazine: magazine_count[ord(c) - ord('a')] += 1 return all(ransom_count[i] <= magazine_count[i] for i in range(26)) ``` (版本二)使用defaultdict ```python from collections import defaultdict class Solution: def canConstruct(self, ransomNote: str, magazine: str) -> bool: hashmap = defaultdict(int) for x in magazine: hashmap[x] += 1 for x in ransomNote: value = hashmap.get(x) if not value or not value: return False else: hashmap[x] -= 1 return True ``` (版本三)使用字典 ```python class Solution: def canConstruct(self, ransomNote: str, magazine: str) -> bool: counts = {} for c in magazine: counts[c] = counts.get(c, 0) + 1 for c in ransomNote: if c not in counts or counts[c] == 0: return False counts[c] -= 1 return True ``` (版本四)使用Counter ```python from collections import Counter class Solution: def canConstruct(self, ransomNote: str, magazine: str) -> bool: return not Counter(ransomNote) - Counter(magazine) ``` (版本五)使用count ```python class Solution: def canConstruct(self, ransomNote: str, magazine: str) -> bool: return all(ransomNote.count(c) <= magazine.count(c) for c in set(ransomNote)) ``` (版本六)使用count(简单易懂) ```python3 class Solution: def canConstruct(self, ransomNote: str, magazine: str) -> bool: for char in ransomNote: if char in magazine and ransomNote.count(char) <= magazine.count(char): continue else: return False return True ``` ### Go: ```go func canConstruct(ransomNote string, magazine string) bool { record := make([]int, 26) for _, v := range magazine { // 通过record数据记录 magazine里各个字符出现次数 record[v-'a']++ } for _, v := range ransomNote { // 遍历ransomNote,在record里对应的字符个数做--操作 record[v-'a']-- if record[v-'a'] < 0 { // 如果小于零说明ransomNote里出现的字符,magazine没有 return false } } return true } ``` ### JavaScript: ```js /** * @param {string} ransomNote * @param {string} magazine * @return {boolean} */ var canConstruct = function(ransomNote, magazine) { const strArr = new Array(26).fill(0), base = "a".charCodeAt(); for(const s of magazine) { // 记录 magazine里各个字符出现次数 strArr[s.charCodeAt() - base]++; } for(const s of ransomNote) { // 对应的字符个数做--操作 const index = s.charCodeAt() - base; if(!strArr[index]) return false; // 如果没记录过直接返回false strArr[index]--; } return true; }; ``` ### TypeScript: ```typescript function canConstruct(ransomNote: string, magazine: string): boolean { let helperArr: number[] = new Array(26).fill(0); let base: number = 'a'.charCodeAt(0); let index: number; for (let i = 0, length = magazine.length; i < length; i++) { helperArr[magazine[i].charCodeAt(0) - base]++; } for (let i = 0, length = ransomNote.length; i < length; i++) { index = ransomNote[i].charCodeAt(0) - base; helperArr[index]--; if (helperArr[index] < 0) { return false; } } return true; }; ``` ### PHP: ```php class Solution { /** * @param String $ransomNote * @param String $magazine * @return Boolean */ function canConstruct($ransomNote, $magazine) { if (count($ransomNote) > count($magazine)) { return false; } $map = []; for ($i = 0; $i < strlen($magazine); $i++) { $map[$magazine[$i]] = ($map[$magazine[$i]] ?? 0) + 1; } for ($i = 0; $i < strlen($ransomNote); $i++) { if (!isset($map[$ransomNote[$i]]) || --$map[$ransomNote[$i]] < 0) { return false; } } return true; } ``` ### Swift: ```swift func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool { var record = Array(repeating: 0, count: 26); let aUnicodeScalarValue = "a".unicodeScalars.first!.value for unicodeScalar in magazine.unicodeScalars { // 通过record 记录 magazine 里各个字符出现的次数 let idx: Int = Int(unicodeScalar.value - aUnicodeScalarValue) record[idx] += 1 } for unicodeScalar in ransomNote.unicodeScalars { // 遍历 ransomNote,在record里对应的字符个数做 -- 操作 let idx: Int = Int(unicodeScalar.value - aUnicodeScalarValue) record[idx] -= 1 // 如果小于零说明在magazine没有 if record[idx] < 0 { return false } } return true } ``` ### Rust: ```rust impl Solution { pub fn can_construct(ransom_note: String, magazine: String) -> bool { let baseChar = 'a'; let mut record = vec![0; 26]; for byte in magazine.bytes() { record[byte as usize - baseChar as usize] += 1; } for byte in ransom_note.bytes() { record[byte as usize - baseChar as usize] -= 1; if record[byte as usize - baseChar as usize] < 0 { return false; } } return true; } } ``` ### Scala: 版本一: 使用数组作为哈希表 ```scala object Solution { def canConstruct(ransomNote: String, magazine: String): Boolean = { // 如果magazine的长度小于ransomNote的长度,必然是false if (magazine.length < ransomNote.length) { return false } // 定义一个数组,存储magazine字符出现的次数 val map: Array[Int] = new Array[Int](26) // 遍历magazine字符串,对应的字符+=1 for (i <- magazine.indices) { map(magazine(i) - 'a') += 1 } // 遍历ransomNote for (i <- ransomNote.indices) { if (map(ransomNote(i) - 'a') > 0) map(ransomNote(i) - 'a') -= 1 else return false } // 如果上面没有返回false,直接返回true,关键字return可以省略 true } } ``` ```scala object Solution { import scala.collection.mutable def canConstruct(ransomNote: String, magazine: String): Boolean = { // 如果magazine的长度小于ransomNote的长度,必然是false if (magazine.length < ransomNote.length) { return false } // 定义map,key是字符,value是字符出现的次数 val map = new mutable.HashMap[Char, Int]() // 遍历magazine,把所有的字符都记录到map里面 for (i <- magazine.indices) { val tmpChar = magazine(i) // 如果map包含该字符,那么对应的value++,否则添加该字符 if (map.contains(tmpChar)) { map.put(tmpChar, map.get(tmpChar).get + 1) } else { map.put(tmpChar, 1) } } // 遍历ransomNote for (i <- ransomNote.indices) { val tmpChar = ransomNote(i) // 如果map包含并且该字符的value大于0,则匹配成功,map对应的--,否则直接返回false if (map.contains(tmpChar) && map.get(tmpChar).get > 0) { map.put(tmpChar, map.get(tmpChar).get - 1) } else { return false } } // 如果上面没有返回false,直接返回true,关键字return可以省略 true } } ``` ### C#: ```csharp public bool CanConstruct(string ransomNote, string magazine) { if(ransomNote.Length > magazine.Length) return false; int[] letters = new int[26]; foreach(char c in magazine){ letters[c-'a']++; } foreach(char c in ransomNote){ letters[c-'a']--; if(letters[c-'a']<0){ return false; } } return true; } ```