1 Star 0 Fork 39

magi02cccc/代码随想录

forked from pakchoi/代码随想录 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
pics
problems
qita
前序
周总结
0001.两数之和.md
0005.最长回文子串.md
0015.三数之和.md
0017.电话号码的字母组合.md
0018.四数之和.md
0019.删除链表的倒数第N个节点.md
0020.有效的括号.md
0024.两两交换链表中的节点.md
0027.移除元素.md
0028.实现strStr.md
0031.下一个排列.md
0034.在排序数组中查找元素的第一个和最后一个位置.md
0035.搜索插入位置.md
0037.解数独.md
0039.组合总和.md
0040.组合总和II.md
0042.接雨水.md
0045.跳跃游戏II.md
0046.全排列.md
0047.全排列II.md
0051.N皇后.md
0052.N皇后II.md
0053.最大子序和.md
0053.最大子序和(动态规划).md
0054.螺旋矩阵.md
0055.跳跃游戏.md
0056.合并区间.md
0059.螺旋矩阵II.md
0062.不同路径.md
0063.不同路径II.md
0070.爬楼梯.md
0070.爬楼梯完全背包版本.md
0072.编辑距离.md
0077.组合.md
0077.组合优化.md
0078.子集.md
0084.柱状图中最大的矩形.md
0090.子集II.md
0093.复原IP地址.md
0096.不同的二叉搜索树.md
0098.验证二叉搜索树.md
0100.相同的树.md
0101.对称二叉树.md
0102.二叉树的层序遍历.md
0104.二叉树的最大深度.md
0106.从中序与后序遍历序列构造二叉树.md
0108.将有序数组转换为二叉搜索树.md
0110.平衡二叉树.md
0111.二叉树的最小深度.md
0112.路径总和.md
0115.不同的子序列.md
0116.填充每个节点的下一个右侧节点指针.md
0121.买卖股票的最佳时机.md
0122.买卖股票的最佳时机II.md
0122.买卖股票的最佳时机II(动态规划).md
0123.买卖股票的最佳时机III.md
0127.单词接龙.md
0129.求根到叶子节点数字之和.md
0131.分割回文串.md
0132.分割回文串II.md
0134.加油站.md
0135.分发糖果.md
0139.单词拆分.md
0141.环形链表.md
0142.环形链表II.md
0143.重排链表.md
0150.逆波兰表达式求值.md
0151.翻转字符串里的单词.md
0160.相交链表.md
0188.买卖股票的最佳时机IV.md
0189.旋转数组.md
0198.打家劫舍.md
0202.快乐数.md
0203.移除链表元素.md
0205.同构字符串.md
0206.翻转链表.md
0209.长度最小的子数组.md
0213.打家劫舍II.md
0216.组合总和III.md
0222.完全二叉树的节点个数.md
0225.用队列实现栈.md
0226.翻转二叉树.md
0232.用栈实现队列.md
0234.回文链表.md
0235.二叉搜索树的最近公共祖先.md
0236.二叉树的最近公共祖先.md
0239.滑动窗口最大值.md
0242.有效的字母异位词.md
0257.二叉树的所有路径.md
0279.完全平方数.md
0283.移动零.md
0300.最长上升子序列.md
0309.最佳买卖股票时机含冷冻期.md
0322.零钱兑换.md
0332.重新安排行程.md
0337.打家劫舍III.md
0343.整数拆分.md
0344.反转字符串.md
0347.前K个高频元素.md
0349.两个数组的交集.md
0376.摆动序列.md
0377.组合总和Ⅳ.md
0383.赎金信.md
0392.判断子序列.md
0404.左叶子之和.md
0406.根据身高重建队列.md
0416.分割等和子集.md
0435.无重叠区间.md
0450.删除二叉搜索树中的节点.md
0452.用最少数量的箭引爆气球.md
0454.四数相加II.md
0455.分发饼干.md
0459.重复的子字符串.md
0463.岛屿的周长.md
0474.一和零.md
0491.递增子序列.md
0494.目标和.md
0496.下一个更大元素I.md
0501.二叉搜索树中的众数.md
0503.下一个更大元素II.md
0509.斐波那契数.md
0513.找树左下角的值.md
0516.最长回文子序列.md
0518.零钱兑换II.md
0530.二叉搜索树的最小绝对差.md
0538.把二叉搜索树转换为累加树.md
0541.反转字符串II.md
0583.两个字符串的删除操作.md
0617.合并二叉树.md
0647.回文子串.md
0649.Dota2参议院.md
0654.最大二叉树.md
0657.机器人能否返回原点.md
0669.修剪二叉搜索树.md
0673.最长递增子序列的个数.md
0674.最长连续递增序列.md
0684.冗余连接.md
0685.冗余连接II.md
0700.二叉搜索树中的搜索.md
0701.二叉搜索树中的插入操作.md
0704.二分查找.md
0707.设计链表.md
0714.买卖股票的最佳时机含手续费.md
0714.买卖股票的最佳时机含手续费(动态规划).md
0718.最长重复子数组.md
0724.寻找数组的中心索引.md
0738.单调递增的数字.md
0739.每日温度.md
0746.使用最小花费爬楼梯.md
0763.划分字母区间.md
0841.钥匙和房间.md
0844.比较含退格的字符串.md
0860.柠檬水找零.md
0922.按奇偶排序数组II.md
0925.长按键入.md
0941.有效的山脉数组.md
0968.监控二叉树.md
0977.有序数组的平方.md
1002.查找常用字符.md
1005.K次取反后最大化的数组和.md
1035.不相交的线.md
1047.删除字符串中的所有相邻重复项.md
1049.最后一块石头的重量II.md
1143.最长公共子序列.md
1207.独一无二的出现次数.md
1221.分割平衡字符串.md
1356.根据数字二进制下1的数目排序.md
1365.有多少小于当前数字的数字.md
1382.将二叉搜索树变平衡.md
O(n)的算法居然超时了,此时的n究竟是多大?.md
为了绝杀编辑距离,卡尔做了三步铺垫.md
二叉树中递归带着回溯.md
二叉树总结篇.md
二叉树理论基础.md
二叉树的统一迭代法.md
二叉树的迭代遍历.md
二叉树的递归遍历.md
关于时间复杂度,你不知道的都在这里!.md
剑指Offer05.替换空格.md
剑指Offer58-II.左旋转字符串.md
动态规划-股票问题总结篇.md
动态规划总结篇.md
动态规划理论基础.md
双指针总结.md
哈希表总结.md
哈希表理论基础.md
回溯总结.md
回溯算法去重问题的另一种写法.md
回溯算法理论基础.md
字符串总结.md
数组总结篇.md
数组理论基础.md
栈与队列总结.md
栈与队列理论基础.md
根据身高重建队列(vector原理讲解).md
算法模板.md
背包总结篇.md
背包理论基础01背包-1.md
背包理论基础01背包-2.md
背包问题理论基础多重背包.md
背包问题理论基础完全背包.md
贪心算法总结篇.md
贪心算法理论基础.md
链表总结篇.md
链表理论基础.md
面试题02.07.链表相交.md
README.md
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
0383.赎金信.md 13.07 KB
一键复制 编辑 原始数据 按行查看 历史
programmercarl 提交于 3年前 . Update

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

在哈希法中有一些场景就是为数组量身定做的。

383. 赎金信

力扣题目链接

给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。

(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)

注意:

你可以假设两个字符串均只含有小写字母。

canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true

思路

这道题目和242.有效的字母异位词很像,242.有效的字母异位词相当于求 字符串a 和 字符串b 是否可以相互组成 ,而这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。

本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。

  • 第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思”  这里说明杂志里面的字母不可重复使用。

  • 第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母,这一点很重要

暴力解法

那么第一个思路其实就是暴力枚举了,两层for循环,不断去寻找,代码如下:

// 时间复杂度: O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        for (int i = 0; i < magazine.length(); i++) {
            for (int j = 0; j < ransomNote.length(); j++) {
                // 在ransomNote中找到和magazine相同的字符
                if (magazine[i] == ransomNote[j]) {
                    ransomNote.erase(ransomNote.begin() + j); // ransomNote删除这个字符
                    break;
                }
            }
        }
        // 如果ransomNote为空,则说明magazine的字符可以组成ransomNote
        if (ransomNote.length() == 0) {
            return true;
        }
        return false;
    }
};

这里时间复杂度是比较高的,而且里面还有一个字符串删除也就是erase的操作,也是费时的,当然这段代码也可以过这道题。

哈希解法

因为题目所只有小写字母,那可以采用空间换取时间的哈希策略, 用一个长度为26的数组还记录magazine里字母出现的次数。

然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。

依然是数组在哈希法中的应用。

一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

代码如下:

// 时间复杂度: O(n)
// 空间复杂度:O(1)
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};
        //add
        if (ransomNote.size() > magazine.size()) {
            return false;
        }
        for (int i = 0; i < magazine.length(); i++) {
            // 通过recode数据记录 magazine里各个字符出现次数
            record[magazine[i]-'a'] ++;
        }
        for (int j = 0; j < ransomNote.length(); j++) {
            // 遍历ransomNote,在record里对应的字符个数做--操作
            record[ransomNote[j]-'a']--;
            // 如果小于零说明ransomNote里出现的字符,magazine没有
            if(record[ransomNote[j]-'a'] < 0) {
                return false;
            }
        }
        return true;
    }
};

其他语言版本

Java:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // 定义一个哈希映射数组
        int[] record = new int[26];

        // 遍历
        for(char c : magazine.toCharArray()){
            record[c - 'a'] += 1;
        }

        for(char c : ransomNote.toCharArray()){
            record[c - 'a'] -= 1;
        }
        
        // 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
        for(int i : record){
            if(i < 0){
                return false;
            }
        }

        return true;
    }
}

Python写法一(使用数组作为哈希表):

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:

        arr = [0] * 26

        for x in magazine:
            arr[ord(x) - ord('a')] += 1

        for x in ransomNote:
            if arr[ord(x) - ord('a')] == 0:
                return False
            else:
                arr[ord(x) - ord('a')] -= 1
        
        return True

Python写法二(使用defaultdict):

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:

        from collections import defaultdict

        hashmap = defaultdict(int)

        for x in magazine:
            hashmap[x] += 1

        for x in ransomNote:
            value = hashmap.get(x)
            if value is None or value == 0:
                return False
            else:
                hashmap[x] -= 1

        return True

Python写法三:

class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        
        # use a dict to store the number of letter occurance in ransomNote
        hashmap = dict()
        for s in ransomNote:
            if s in hashmap:
                hashmap[s] += 1
            else:
                hashmap[s] = 1
        
        # check if the letter we need can be found in magazine
        for l in magazine:
            if l in hashmap:
                hashmap[l] -= 1
        
        for key in hashmap:
            if hashmap[key] > 0:
                return False
        
        return True

Python写法四:

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        c1 = collections.Counter(ransomNote)
        c2 = collections.Counter(magazine)
        x = c1 - c2
        #x只保留值大于0的符号,当c1里面的符号个数小于c2时,不会被保留
        #所以x只保留下了,magazine不能表达的
        if(len(x)==0):
            return True
        else:
            return False

Go:

func canConstruct(ransomNote string, magazine string) bool {
    record := make([]int, 26)
    for _, v := range magazine {
        record[v-'a']++
    }
    for _, v := range ransomNote {
        record[v-'a']--
        if record[v-'a'] < 0 {
            return false
        }
    }
    return true
}

javaScript:

/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
    const strArr = new Array(26).fill(0), 
        base = "a".charCodeAt();
    for(const s of magazine) {
        strArr[s.charCodeAt() - base]++;
    }
    for(const s of ransomNote) {
        const index = s.charCodeAt() - base;
        if(!strArr[index]) return false;
        strArr[index]--;
    }
    return true;
};

TypeScript:

function canConstruct(ransomNote: string, magazine: string): boolean {
    let helperArr: number[] = new Array(26).fill(0);
    let base: number = 'a'.charCodeAt(0);
    let index: number;
    for (let i = 0, length = magazine.length; i < length; i++) {
        helperArr[magazine[i].charCodeAt(0) - base]++;
    }
    for (let i = 0, length = ransomNote.length; i < length; i++) {
        index = ransomNote[i].charCodeAt(0) - base;
        helperArr[index]--;
        if (helperArr[index] < 0) {
            return false;
        }
    }
    return true;
};

PHP:

class Solution {
    /**
     * @param String $ransomNote
     * @param String $magazine
     * @return Boolean
     */
    function canConstruct($ransomNote, $magazine) {
        if (count($ransomNote) > count($magazine)) {
            return false;
        }
        $map = [];
        for ($i = 0; $i < strlen($magazine); $i++) {
            $map[$magazine[$i]] = ($map[$magazine[$i]] ?? 0) + 1;
        }
        for ($i = 0; $i < strlen($ransomNote); $i++) {
            if (!isset($map[$ransomNote[$i]]) || --$map[$ransomNote[$i]] < 0) {
                return false;
            }
        }
        return true;
    }

Swift:

func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool {
    var record = Array(repeating: 0, count: 26);
    let aUnicodeScalarValue = "a".unicodeScalars.first!.value
    for unicodeScalar in magazine.unicodeScalars {
        // 通过record 记录 magazine 里各个字符出现的次数
        let idx: Int = Int(unicodeScalar.value - aUnicodeScalarValue)
        record[idx] += 1
    }
    for unicodeScalar in ransomNote.unicodeScalars {
        // 遍历 ransomNote,在record里对应的字符个数做 -- 操作
        let idx: Int = Int(unicodeScalar.value - aUnicodeScalarValue)
        record[idx] -= 1
        // 如果小于零说明在magazine没有
        if record[idx] < 0 {
            return false
        }
    }
    return true
}

Rust:

impl Solution {
    pub fn can_construct(ransom_note: String, magazine: String) -> bool {
        let baseChar = 'a';
        let mut record = vec![0; 26];
        
        for byte in magazine.bytes() {
            record[byte as usize - baseChar as usize] += 1;
        }
        
        for byte in ransom_note.bytes() {
            record[byte as usize - baseChar as usize] -= 1;
            if record[byte as usize - baseChar as usize] < 0 {
                return false;
            }
        }
        
        return true;
    }
}

Scala:

版本一: 使用数组作为哈希表

object Solution {
  def canConstruct(ransomNote: String, magazine: String): Boolean = {
    // 如果magazine的长度小于ransomNote的长度,必然是false
    if (magazine.length < ransomNote.length) {
      return false
    }
    // 定义一个数组,存储magazine字符出现的次数
    val map: Array[Int] = new Array[Int](26)
    // 遍历magazine字符串,对应的字符+=1
    for (i <- magazine.indices) {
      map(magazine(i) - 'a') += 1
    }
    // 遍历ransomNote
    for (i <- ransomNote.indices) {
      if (map(ransomNote(i) - 'a') > 0)
        map(ransomNote(i) - 'a') -= 1
      else return false
    }
    // 如果上面没有返回false,直接返回true,关键字return可以省略
    true
  }
}
object Solution {
  import scala.collection.mutable
  def canConstruct(ransomNote: String, magazine: String): Boolean = {
    // 如果magazine的长度小于ransomNote的长度,必然是false
    if (magazine.length < ransomNote.length) {
      return false
    }
    // 定义map,key是字符,value是字符出现的次数
    val map = new mutable.HashMap[Char, Int]()
    // 遍历magazine,把所有的字符都记录到map里面
    for (i <- magazine.indices) {
      val tmpChar = magazine(i)
      // 如果map包含该字符,那么对应的value++,否则添加该字符
      if (map.contains(tmpChar)) {
        map.put(tmpChar, map.get(tmpChar).get + 1)
      } else {
        map.put(tmpChar, 1)
      }
    }
    // 遍历ransomNote
    for (i <- ransomNote.indices) {
      val tmpChar = ransomNote(i)
      // 如果map包含并且该字符的value大于0,则匹配成功,map对应的--,否则直接返回false
      if (map.contains(tmpChar) && map.get(tmpChar).get > 0) {
        map.put(tmpChar, map.get(tmpChar).get - 1)
      } else {
        return false
      }
    }
    // 如果上面没有返回false,直接返回true,关键字return可以省略
    true
  }
}

C#:

public bool CanConstruct(string ransomNote, string magazine) {
        if(ransomNote.Length > magazine.Length) return false;
        int[] letters = new int[26]; 
        foreach(char c in magazine){
            letters[c-'a']++;
        }
        foreach(char c in ransomNote){
            letters[c-'a']--;
            if(letters[c-'a']<0){
                return false;
            }
        }
        return true;
    }


Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/nybaba/leetcode-master.git
git@gitee.com:nybaba/leetcode-master.git
nybaba
leetcode-master
代码随想录
master

搜索帮助