LeetCode 383 赎金信

1. 题目

383. 赎金信

题目描述

给你两个字符串:ransomNote 和 magazine ,判断 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. 使用大写 / 混合字符时未做范围判断(本题限定全小写无需处理)。

相关推荐
飞Link7 小时前
什么是 AirSim?一文读懂无人机仿真平台
python·算法·无人机
一去不复返的通信er9 小时前
贪心算法-合并区间(LeetCode 56)
算法·leetcode·贪心算法
Shaoshing10 小时前
LeetCode100 49. 字母异位词分组
java·算法
YFJ_mily10 小时前
第二届区块链技术与基础模型国际学术会议(BTFM2027)
算法·区块链·能源·智能系统·rdlink研发家·机器人自动化
汤姆宝库11 小时前
收官深拆:第一性原理、产业电网与未来推演
大数据·人工智能·算法
moshuying12 小时前
块扩散 + GDN 改造:Clover 把「并行生成」塞进了 Agent 的长上下文里
前端·算法
没落之王13 小时前
历史系统建模:宋初太祖太宗「再立天庭」秩序重构推演
人工智能·算法·重构
INGNIGHT13 小时前
91 · 最小调整代价(序列dp)
数据结构·算法·leetcode
moshuying13 小时前
扩散模型开始写代码、开网页了:350 亿参数,速度比 Jev 快一倍
前端·算法