代码随想录刷题-哈希表-赎金信

文章讲述了如何使用哈希表解决LeetCode上的383.赎金信问题。通过遍历杂志字符串构建字符计数,然后遍历赎金信检查字符是否可用。文章提到了数组和map两种解法,其中数组解法在空间复杂度上更优。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

赎金信

本节对应代码随想录中:代码随想录,讲解视频:暂无

昨天写好忘了发到优快云了o(╥﹏╥)o

习题

题目链接:383. 赎金信 - 力扣(LeetCode)

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:
输入:ransomNote = “aa”, magazine = “ab”
输出:false

示例 2:
输入:ransomNote = “aa”, magazine = “aab”
输出:true

提示:

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNote 和 magazine 由小写英文字母组成

哈希表

这道简单题和之前的有效的字母异位词那题差不多,是写的最快的一道题了。

判断 ransomNote 能不能由 magazine 构成,只需要先遍历下 magazine 将每个字符出现的次数存起来,然后再遍历 ransomNote,遇到每个字符将其计数-1,如果计数小于0说明 ransomNote 中的这个字符比 magazine 的至少多一个,即不能由 magazine 构成。而如果遍历完 ransomNote 也没能找到计数小于0的则说明可以由 magazine 构成。

查找元素那肯定是用哈希表,解题的关键是选择数组、set、map 中的哪一个更优?首先,set 只能存放 key,而我们还要计数,那肯定是不合适的。而题目中说了 ransomNote 和 magazine 由小写英文字母组成,这就说明最多有26个字符,那么用一个26的数组就更合适。当然,也可以用 map,但是 map 要维护哈希表比数组费时,所以数组更优,后面我会给出两种解法的代码和比较。

首先是数组的解法

class Solution {
   public:
    bool canConstruct(string ransomNote, string magazine) {
        int nums[26] = {0};
        // 先遍历magazine计算每个字符的出现次数
        for (int i = 0; i < magazine.length(); i++) {
            nums[magazine[i] - 'a']++;  // 根据-'a'计算相对位置
        }
        // 再遍历ransomNote
        for (int i = 0; i < ransomNote.length(); i++) {
            // 先将次数-1,这样如果<0(0-1<0)说明比magazine至少多一个
            nums[ransomNote[i] - 'a']--;
            if (nums[ransomNote[i] - 'a'] < 0) {
                return false;
            }
        }
        return true;
    }
};
  • 时间复杂度:O(n)。其中 N 是 magazine 的长度,因为遍历 magazine 可以花费 O(N) 的时间,并且对于每个字符,可以在常数时间内更新 nums 数组。然后我们再遍历 ransomNote,有 N 个字符,对于每个字符,我们需要常数时间检查 nums 中是否存在当前字符的频率,因此总时间复杂度为O(N)
  • 空间复杂度:O(1)。因为 nums 数组的大小始终都是常数级别的 (26)

优化:可以在最前面加一个判断,如果 ransomNote 比 magazine 更长,那一定无法由 magazine 构成,返回 false

map 的解法如下

class Solution {
   public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map<char, int> hash_map;
        // 先遍历magazine计算每个字符的出现次数
        for (int i = 0; i < magazine.length(); i++) {
            hash_map[magazine[i]]++;    // 直接将相应元素做为key
        }
        // 再遍历ransomNote
        for (int i = 0; i < ransomNote.length(); i++) {
            // 先将次数-1,这样如果<0(0-1<0)说明比magazine至少多一个
            hash_map[ransomNote[i]]--;
            if (hash_map[ransomNote[i]] < 0) {
                return false;
            }      
        }
        return true;
    }
};
  • 时间复杂度:O(n)。其中n是magazine的长度加上ransomNote的长度,因为代码中包含两个for循环,每个循环执行次数不超过输入字符串的长度
  • 空间复杂度:O(m)。其中 m 是 magazine 中不同字符的个数。在最坏的情况下,哈希表将包含 magazine 中的全部字符

可以看到两个解法其实就是存的 key 不太一样,数组用相对位置当 key,而 map 直接用对应元素当 key。下面是 LeetCode 两种解法的提交记录,从图中也可以看出数组的解法更优

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值