383. 赎金信

17 阅读2分钟

题目

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

给你两个字符串: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 由小写英文字母组成

思路

解题思路参考242. 有效的字母异位词

但需要注意的是,需要先遍历 magazine ,在遍历 magazin 时对数组元素进行加一操作;然后在遍历 ransomNote 时对数组元素进行减一操作。最后只要数组中元素均大于等于 0 即为成功。

若在遍历 ransomNote 时对数组元素进行加一操作,在遍历 magazin 时对数组进行减一操作,然后依据数组元素是否均大于等于 0 来判断。得到的答案是错误的。可以将 ransomNote = "aa", magazine = "aab" 带入进行测试。当 ransomNote = "aa", magazine = "aab" 时,即使 magazine 满足条件,数组中会存在小于 0 的元素。

代码

function canConstruct(ransomNote: string, magazine: string): boolean {
    // 分别计算两个字符串的长度
    const ransomNoteLens = ransomNote.length;
    const magazineLens = magazine.length;

    // 如果长度不同,直接返回 false
    if (ransomNoteLens > magazineLens) return false;
    
    // 创建一个长度为26的数组,且每个元素赋值为0
    const assicArr = new Array(26).fill(0);
    // 获取字符a的ASCII码
    // 因为英文小写a~z的ASCII码并不是从0-26的
    // 所以将要遍历的字符串中的字符的ASCII码都要减去 a 的ASCII码
    // 这样就可以从将这些英文字符从0开始编码,符合我们创建的数组
    const baseCode = "a".charCodeAt(0);

    // 将对应元素加一
    // 注意,这里要先遍历 magazine 中的字符
    // 因为 magazine 长度是大于 ransomNote
    for (let i = 0; i < magazineLens; i++) {
        // 获取字符在数组中的位置
        const gap = magazine.charCodeAt(i) - baseCode;
        // 将对应为位置的元素加一
        assicArr[gap]++;
    }

    // 将对应元素减一
    // 当遍历 ransomNote 的时候,对数组元素进行减一操作
    for (let j = 0; j < ransomNoteLens; j++) {
        // 获取字符在数组中的位置
        const gap = ransomNote.charCodeAt(j) - baseCode;
        // // 将对应为位置的元素减一
        assicArr[gap]--;

        // 遍历 ransomNote 字符时,若发现有小于0的
        // 说明 magazine 中一定不包含 ransomNote 中所需字符
        if (assicArr[gap] < 0) return false;
    }

    
    return true;
};
  • 时间复杂度: O(n)

    遍历了 m + n 次,其中 n <= ransomNoteLens。

  • 空间复杂度: O(1)