46- 赎金信

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

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

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

示例 1:

复制代码
输入:ransomNote = "a", magazine = "b"
输出:false

示例 2:

复制代码
输入:ransomNote = "aa", magazine = "ab"
输出:false

示例 3:

复制代码
输入:ransomNote = "aa", magazine = "aab"
输出:true

提示:

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNote 和 magazine 由小写英文字母组成
方法一:暴力匹配法

这种方法通过逐个检查 ransomNote 中的字符是否存在于 magazine 中,并在找到后从 magazine 中移除该字符,确保每个字符只能使用一次。

TypeScript 复制代码
function canConstruct(ransomNote: string, magazine: string): boolean {
    // 将magazine转换为数组,方便删除操作
    const magArray: string[] = magazine.split('');
    
    // 遍历ransomNote中的每个字符
    for (let i = 0; i < ransomNote.length; i++) {
        const char = ransomNote[i];
        // 查找字符在magArray中的索引
        const index = magArray.indexOf(char);
        // 如果没找到,返回false
        if (index === -1) {
            return false;
        }
        // 从magArray中移除找到的字符
        magArray.splice(index, 1);
    }
    // 所有字符都找到,返回true
    return true;
}
方法二:哈希表统计法

使用哈希表分别统计 ransomNote 和 magazine 中各字符的出现次数,然后比较 ransomNote 中每个字符的次数是否不超过 magazine 中的次数。

TypeScript 复制代码
function canConstruct(ransomNote: string, magazine: string): boolean {
    // 定义统计频率的函数
    function countFrequency(str: string): Map<string, number> {
        const freqMap = new Map<string, number>();
        for (const char of str) {
            freqMap.set(char, (freqMap.get(char) || 0) + 1);
        }
        return freqMap;
    }
    
    // 统计两个字符串的字符频率
    const ransomFreq = countFrequency(ransomNote);
    const magFreq = countFrequency(magazine);
    
    // 检查ransomNote中的每个字符频率是否超过magazine
    for (const [char, count] of ransomFreq.entries()) {
        if (!magFreq.has(char) || magFreq.get(char)! < count) {
            return false;
        }
    }
    return true;
}
方法三:数组统计法

由于题目中字符是小写英文字母,可使用长度为 26 的数组代替哈希表,提高效率。

TypeScript 复制代码
function canConstruct(ransomNote: string, magazine: string): boolean {
    // 创建长度为26的数组,对应a-z的频率
    const freqArray: number[] = new Array(26).fill(0);
    
    // 统计magazine中各字符的频率
    for (const char of magazine) {
        freqArray[char.charCodeAt(0) - 'a'.charCodeAt(0)]++;
    }
    
    // 检查ransomNote中的字符频率
    for (const char of ransomNote) {
        const index = char.charCodeAt(0) - 'a'.charCodeAt(0);
        // 如果字符不存在或频率已用完,返回false
        if (freqArray[index] === 0) {
            return false;
        }
        // 减少对应字符的频率
        freqArray[index]--;
    }
    return true;
}

复杂度分析

方法 时间复杂度 空间复杂度 说明
方法一(暴力法) O(m×n) O(m) m 是 magazine 长度,n 是 ransomNote 长度。每次查找和删除都是 O (m) 操作
方法二(哈希表) O(m+n) O(k) k 是不同字符的数量,最多 26 个。统计和比较都是线性时间
方法三(数组法) O(m+n) O(1) 使用固定长度数组,空间复杂度为常数,是三种方法中效率最高的

测试示例

TypeScript 复制代码
// 示例1测试
console.log(canConstruct("a", "b"));  // 输出: false

// 示例2测试
console.log(canConstruct("aa", "ab"));  // 输出: false

// 示例3测试
console.log(canConstruct("aa", "aab"));  // 输出: true

方法说明

  • 暴力法是最直观的解法,但效率最低,因为每次查找和删除操作都是 O (m) 时间复杂度,m 是 magazine 的长度。

  • 哈希表法通过统计字符频率进行比较,时间复杂度优化到 O (m+n),空间复杂度取决于不同字符的数量,适用于各种字符集的情况。

  • 数组法是针对本题的最优解法,利用字符范围固定(小写字母)的特点,使用固定长度数组统计频率,空间复杂度为 O (1),时间复杂度也是线性的,是三种方法中效率最高的。

在实际应用中,如果字符集已知且范围较小,应优先选择数组法;如果字符集不确定,则使用哈希表法更为通用。

相关推荐
民乐团扒谱机1 小时前
【微实验】NSGA-II(非支配排序遗传算法)——解决多目标优化问题的“自然选择模拟器”
linux·人工智能·算法
青少儿编程课堂1 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:中缀表达式求值解析
c++·python·算法·bfs·信息学竞赛
武子康3 小时前
Qwen-Drive 到底是什么:4B 级 VLM 如何成为驾驶策略底座
人工智能·算法·机器学习
zh_xuan5 小时前
LeeCode 496. 下一个更大元素I
数据结构·算法·栈·单调栈
机 _ 长5 小时前
05 智算运维|8 张卡怎么才算「捆成一张」:NCCL 实测与拓扑
运维·人工智能·算法·智算运维
早起CaiCai6 小时前
【2023-01 OM】利用深度学习算法融合异质观测实现海洋场降尺度
人工智能·深度学习·算法
梦里寻码6 小时前
我用 NestJS + Vue3 写了一套完整的研发管理系统(58 表 / 229 API / 130 项断言,全开源
前端·后端·算法
猿人谷7 小时前
Surrogate Model:让进化算法学会“预测”,为什么它正在成为智能优化的重要技术?
后端·算法·架构
青少儿编程课堂8 小时前
蓝桥杯青少组图形化 STEMA 省考(报名倒计时·11/22开考)+ 全国青少年信息素养大赛 2026—2027赛季 + 电子学会图形化考级:校园华容道大闯关
c++·python·算法·bfs·信息学竞赛