2021-12-04每日刷题打卡

本文探讨了力扣上的两个编程挑战:赎金信问题和寻找棋盘上最大价值路径。对于赎金信问题,解决方案是利用哈希表记录字符出现次数;而对于最大价值路径,采用动态规划策略,从棋盘左上角到右下角,逐次计算每个位置的最大价值。动态规划在解决这类问题中表现出高效和精准的特点。
摘要由CSDN通过智能技术生成

2021-12-04每日刷题打卡

力扣——每日一题

383. 赎金信

为了不在赎金信中暴露字迹,从杂志上搜索各个需要的字母,组成单词来表达意思。

给你一个赎金信 (ransomNote) 字符串和一个杂志(magazine)字符串,判断 ransomNote 能不能由 magazines 里面的字符构成。

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

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

示例 1:

输入:ransomNote = “a”, magazine = “b”
输出:false

简简单单的哈希,开辟两个长度为26的数组r、m,对应26个字母的位置,遍历ransomNote和magazines,把相应字母的位置加上1,比如magazines是aab的话,就把m[0]的数+2,m[1]的数+1。然后开始遍历r数组,如果r[i]不为0,就判断m[i]是否大于r[i],如果大于则说明可以组成,如果小于说明不能组成,直接返回false。当遍历结束后如果一直没有返回false说明magazines可以组成赎金信,返回true。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        vector<int>r(26);
        vector<int>m(26);
        for(auto i:ransomNote)
            r[i-'a']++;
        for(auto i:magazine)
            m[i-'a']++;
        for(int i=0;i<26;i++)
        {
            if(r[i]>0&&r[i]-m[i]>0)
            {
                return false;
            }
        }
        return true;
    }
};

力扣——剑指offer

剑指 Offer 47. 礼物的最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

示例 1:

输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

这里用的是动态规划的思想,我们可以想一想,如何一步走完不回头的情况下拿到最大价值的礼物,那我们就应该把所有路径都试过一遍然后取最大的那一个,但这样数据量大了肯定会超时,但我们可以优化一下它,比如在这里,我们只能往下或往右走,第一列一直往下和第一行一直往右的价值是确定了的,如果一直往右,第一行的价值就是1、1+3、1+3+1,第一列的价值就是1、1+1、1+1+4,每个位置的价值总和等于上一个位置的价值总和和自身价值的和。但如果在第二行第二列,此时出现了两种情况,我们可以从0 0开始先右走再下走走到1 1,也可以先下走再右走走到1 1,这两条路上的价值总和是不同的,那么,我们只要选择价值大的一条路即可,比如这里,先右走的价值总和为4,先下走的价值总和为2,显然我们选择的是先右再下,加上1 1位置自身的价值,1 1位置上可以达到的最大价值为1+3+5=9,然后下一个位置可以如法炮制,比如1 2,我们可以先右走到头再下,或者按照我们刚刚的路径再往右走,我们不用考虑先下走然后右走到头这个路径了,因为刚刚已经在1 1位置上被我们排除掉了,然后我们再比较最大值来决定怎么到达1 2位置,其它位置也都是如此,只要比较上面和左边的元素,选较大的那个加在自己身上即可,最后2 2位置(终点)的值就会是总和最大的了。

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int n=grid.size(),m=grid[0].size();
        vector<vector<int>>dp(n,vector<int>(m));
        dp[0][0]=grid[0][0];
        for(int i=1;i<m;i++)dp[0][i]+=dp[0][i-1]+grid[0][i];
        for(int i=1;i<n;i++)dp[i][0]+=dp[i-1][0]+grid[i][0];
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<m;j++)
            {
                dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[n-1][m-1];
    }
};
剑指 Offer 48. 最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

动态规划,我们准备一个长度和字符串长度一样的数组dp,dp存的是各个位置上的最大不包含重复字符的长度,第一个位置必然不会重复,我们先初始设为1,然后从i=1开始遍历字符串,每次用双指针遍历的方法向前遍历,准备一个指针j=i-1,只要s[j]!=s[i],j就一直–,知道s[j]==s[i]或者j<0为止,然后比较一下dp[i-1]和i-j的大小,如果i-j大,说明i到j这一段路中间是有重复字符出现的,比如这里的样例,如果i在e的位置上,j会因为一直没法和i相等而一直–,最后j会变成-1,此时i-j为5,但我们知道中间有两个ww重复了,是不满足条件的,所有我们不能取i-j给dp[i],而是要取dp[i-1]+1给dp[i],因为重复ww的问题在第二个w时就已经解决了,我们dp存的是不重复的最大长度,如果i-j大于dp[i-1],说明i到j一定有重复的字符,而dp[i-1]+1是因为算上s[i]位置上的字符,最大长度要+1,遍历字符串的时候可以准备一个遍历ans维护最大长度,每次把ans和dp[i]做对比,取较大的给ans,最后返回ans即可。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n=s.size();
        if(n<2)return n;
        vector<int>dp(n);
        dp[0]=1;
        int ans=1;
        for(int i=1;i<n;i++)
        {
            int j=i-1;
            while(j>=0&&s[j]!=s[i])j--;
            dp[i]=dp[i-1]<i-j?dp[i-1]+1:i-j;
            ans=max(ans,dp[i]);
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值