LeetCode 383 赎金信

1. 题目

383. 赎金信

题目描述

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false

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

示例

输入:ransomNote = "a", magazine = "b"

输出:false

输入:ransomNote = "aa", magazine = "ab"

输出:false

输入:ransomNote = "aa", magazine = "aab"

输出:true

约束

  • (1 <= ransomNote.length, magazine.length <= 10^5)

  • 两个字符串均由小写英文字母组成

2. 最佳解题思路描述(字母计数数组,最优)

  1. 只有 26 个小写字母,开辟长度 26 的数组统计字符出现次数;

  2. 先遍历 magazine,对应字母位置计数 + 1;

  3. 再遍历 ransomNote,对应字母位置计数 - 1;

  4. 最后遍历计数数组,若存在任意位置数值小于 0,说明赎金信需要的字符数量多于杂志拥有,返回 false;全部≥0 则返回 true。

优势

  • 时间复杂度 (O(m+n)),m、n 为两个字符串长度,仅两次遍历;

  • 空间复杂度 (O(1)),固定长度 26 数组,常数空间;

  • 相比哈希表 unordered_map,数组寻址更快、无哈希冲突开销,本题首选。

3. 可优化代码(逻辑完全标准最优,仅微小格式优化)

复制代码
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int Arr[26] = {0};
        for(char c : magazine){
            Arr[c-'a']++;
        }
        for(char c : ransomNote){
            Arr[c-'a']--;
        }
        for(int i : Arr){
            if(i<0){
                return false;
            }
        }
        return true;
    }
};

代码说明

  1. 正确性:无任何逻辑 bug,能通过全部测试用例,是本题标准最优解法;

  2. 细微优化点(不影响 AC,仅提升规范度):

    • 数组名 Arr 语义模糊,改为 cnt / count 更易懂;

    • 可在遍历赎金信时提前判断,计数减为 0 直接返回 false,省去最后一轮完整遍历;

    • 变量、循环增加空格,代码可读性更好。

4. 规范优化版(提前终止,减少一轮遍历)

复制代码
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int cnt[26] = {0};
        // 统计杂志字符
        for (char c : magazine) {
            cnt[c - 'a']++;
        }
        // 匹配赎金信,中途不足直接返回false
        for (char c : ransomNote) {
            int idx = c - 'a';
            cnt[idx]--;
            if (cnt[idx] < 0) {
                return false;
            }
        }
        return true;
    }
};

5. 总结

  1. 数组计数思路完全正确、性能最优;

  2. 核心逻辑:杂志字符做加法,赎金信字符做减法,负数代表字符不够;

  3. 26 个小写字母固定场景,优先用数组而非哈希表;

  4. 优化小技巧:遍历赎金信时实时判断,提前 return,不用完整遍历 26 个数字。

6. 相关知识拓展

拓展 1:哈希表写法(不推荐,开销更大)

字符种类少时数组完胜哈希,仅字符范围大时选用:

复制代码
#include <unordered_map>
bool canConstruct(string ransomNote, string magazine) {
    unordered_map<char, int> mp;
    for (char c : magazine) mp[c]++;
    for (char c : ransomNote) {
        mp[c]--;
        if (mp[c] < 0) return false;
    }
    return true;
}

拓展 2:同类字符计数模板题

  • LC242 有效的字母异位词(同样 26 数组统计,最终全部为 0)

  • LC438 找到字符串中所有字母异位词(滑动窗口 + 26 计数数组)

  • LC125 验证回文串(字符筛选 + 计数)

拓展 3:复杂度对比

  1. 26 计数数组(优化后):(O(m+n)) 时间,(O(1)) 空间(最优);

  2. unordered_map 哈希表:(O(m+n)) 时间,(O(1)) 理论空间,但常数开销更大;

拓展 4:易错点复盘

  1. 颠倒遍历顺序:先遍历 ransomNote 再加 magazine,逻辑完全颠倒;

  2. 忘记判断 <0,只判断等于 0,无法识别字符数量不足;

  3. 使用大写 / 混合字符时未做范围判断(本题限定全小写无需处理)。

相关推荐
维克兜率天1 小时前
5.3 宏观因子:抬头看天
笔记·python·深度学习·算法·量化
LuminousCPP1 小时前
数据结构-排序(五):计数排序与排序专题阶段总结|从外部归并到线性排序,再看算法边界与选型
c语言·数据结构·笔记·算法·排序算法
流云枫1 小时前
003-上下文窗口:物理限制、KV Cache 与工程应对策略
算法
盟道科技1 小时前
小程序订阅消息大规模投递实践:频控令牌池、Redis限流与失败补偿设计
分布式·算法·小程序
工具派1 小时前
视频场景检测是怎么找到切镜头位置的?三种思路与实测
算法·计算机视觉·视频
钓鱼的肝1 小时前
csp-j-s总结(1)
c++·笔记·算法·青少年编程
流云枫1 小时前
001-Transformer架构与Self-Attention机制深度解析
算法
生信大杂烩2 小时前
Xenium H&E空间原位可视化——细胞轮廓、基因表达与转录本可视化
python·算法·数据分析
linx2952 小时前
第七章 · 标准库容器、算法与 ranges
c语言·开发语言·数据结构·c++·算法