LeetCode 单周赛298,前三题

2309. 兼具大小写的最好英文字母

给你一个由英文字母组成的字符串 s ,请你找出并返回 s 中的 最好 英文字母。返回的字母必须为大写形式。如果不存在满足条件的字母,则返回一个空字符串。

最好 英文字母的大写和小写形式必须 都 在 s 中出现。

英文字母 b 比另一个英文字母 a 更好 的前提是:英文字母表中,b 在 a 之 后 出现。

示例 1:

输入:s = “lEeTcOdE”
输出:“E”
解释:
字母 ‘E’ 是唯一一个大写和小写形式都出现的字母。

示例 2:

输入:s = “arRAzFif”
输出:“R”
解释:
字母 ‘R’ 是大写和小写形式都出现的最好英文字母。
注意 ‘A’ 和 ‘F’ 的大写和小写形式也都出现了,但是 ‘R’ 比 ‘F’ 和 ‘A’ 更好。

示例 3:

输入:s = “AbCdEfGhIjK”
输出:“”
解释:
不存在大写和小写形式都出现的字母。

提示:

1 <= s.length <= 1000
s 由小写和大写英文字母组成

思路

遍历一遍字符串,只要是字母就标记,之后遍历标记数组,从后往前判断,大小写都有的,第一个符合的也是最大的

代码

class Solution {
public:
    int t[100];
    string greatestLetter(string s) {
        string res = "";
        for (int i = 0; i < s.size(); i++) {
            t[s[i] - 'A'] = 1;
        }
        for (int i = 0; i < 26; i++) {
            if (t[i] == 1 && t[i + 32] == 1) {
                res = i + 'A';
            }
        }
        
        return res;
    }
};

2310. 个位数字为 K 的整数之和

给你两个整数 num 和 k ,考虑具有以下属性的正整数多重集:

每个整数个位数字都是 k 。
所有整数之和是 num 。
返回该多重集的最小大小,如果不存在这样的多重集,返回 -1 。

注意:

多重集与集合类似,但多重集可以包含多个同一整数,空多重集的和为 0 。
个位数字 是数字最右边的数位。

示例 1:

输入:num = 58, k = 9
输出:2
解释:
多重集 [9,49] 满足题目条件,和为 58 且每个整数的个位数字是 9 。
另一个满足条件的多重集是 [19,39] 。
可以证明 2 是满足题目条件的多重集的最小长度。

示例 2:

输入:num = 37, k = 2
输出:-1
解释:个位数字为 2 的整数无法相加得到 37 。

示例 3:

输入:num = 0, k = 7
输出:0
解释:空多重集的和为 0 。

提示:

0 <= num <= 3000
0 <= k <= 9

贪心

拆解,nk + 10t == num

(num - nk) % 10 == 0

class Solution {
public:
    int minimumNumbers(int num, int k) {
        if (num == 0) return 0;
        // 拆解,nk + 10t == num
        // (num - nk) % 10 == 0

        for (int i = 1; i <= num && num - k * i >= 0; i++) { 
            if ((num - k * i) % 10 == 0) {
                return i; // 因为是从小到大模拟的,第一个遇到的合适的条件就是最小值
            }
        }

        return -1;
    }
};

动态规划1

当num == 0 时,返回0,如果k是偶数,但是num是奇数,直接返回-1,不可能
先挑选出来个位是k的数,组成一个数组,作为背包,然后一维滚动数组,动态规划
外层遍历物品,内层遍历背包容量
初始化,dp[0] = 0,因为num是0的时候,由0个数字组成,要取组成num的数的个数的最小值,所以数组初始化为无穷大
dp[j] = min(dp[j - i] + 1, dp[j]),要么就取j-i的数量 + 1,要么就取 dp[j]

class Solution {
public:
    int bg[3010];
    int dp[3010];
    int minimumNumbers(int num, int k) {
        if (num == 0) return 0;
        if (num % 2 == 1 && k % 2 == 0) return -1;
        int cnt = 0;
        for (int i = 1; i <= num; i++) {
            if (i % 10 == k) bg[cnt++] = i;
        }
        for (int i = 1; i <= num; i++) dp[i] = 99999;
        dp[0] = 0;
        for (int i = 0; i < cnt; i++) {
            for (int j = bg[i]; j <= num; j++) {
                dp[j] = min(dp[j - bg[i]] + 1, dp[j]);
            }
        }
        if (dp[num] == 99999) return -1;
        return dp[num];
        
    }
};

动态规划2

class Solution {
    int dp[3001];
public:
    int minimumNumbers(int num, int k) {
        memset(dp, 127, sizeof(dp)); // 求最小,初始无穷大

        dp[0] = 0;
        for (int i = 0; i <= num; i++) {
            for (int j = i; j <= num; j++) {
                if (i % 10 == k)
                    dp[j] = min(dp[j - i] + 1, dp[j]);
            }
        }
        if (dp[num] > 1 << 30) return -1;
        return dp[num];
    }
};

2311. 小于等于 K 的最长二进制子序列

给你一个二进制字符串 s 和一个正整数 k 。

请你返回 s 的 最长 子序列,且该子序列对应的 二进制 数字小于等于 k 。

注意:

子序列可以有 前导 0 。
空字符串视为 0 。
子序列 是指从一个字符串中删除零个或者多个字符后,不改变顺序得到的剩余字符序列。

示例 1:

输入:s = “1001010”, k = 5
输出:5
解释:s 中小于等于 5 的最长子序列是 “00010” ,对应的十进制数字是 2 。
注意 “00100” 和 “00101” 也是可行的最长子序列,十进制分别对应 4 和 5 。
最长子序列的长度为 5 ,所以返回 5 。

示例 2:

输入:s = “00101001”, k = 1
输出:6
解释:“000001” 是 s 中小于等于 1 的最长子序列,对应的十进制数字是 1 。
最长子序列的长度为 6 ,所以返回 6 。

提示:

1 <= s.length <= 1000
s[i] 要么是 ‘0’ ,要么是 ‘1’ 。
1 <= k <= 109

贪心?

设置一个bool数组初始化为 false
因为可以有前导0,所以先把所有的零都选上,ans记录一下现在的个数
然后,因为1在右边加的少,在左边加的太多了,所以从右往左加,看看这个1加上之后会不会超

class Solution {
    bool b[1001];
public:
    bool check(string &s, int k) {
        int n = s.size(), x = 0;
        for (int i = 1; i <= n; i++) {
            if (b[i])
                x = x * 2 + s[i - 1] - '0';
            if (x > k)
                return false;
        }
        return true;
    }
    int longestSubsequence(string s, int k) {
        memset(b, false, sizeof(b));
        int n = s.size();
        int ans = 0; // 记录数字的个数
        
        for (int i = 1; i <= n; i++) { // 首先,加上所有的 0 
            if (s[i - 1] == '0')
                ++ans, b[i] = true; // 记录当前数字的个数,并且把选中的数字改成 true 
        }
        
        for (int i = n; i > 0; i--)
            if (!b[i]) {    // 从后往前遍历,将刚才没有加进去的 1 
                b[i] = true;    // 先变成true,方便下面计算
                if (!check(s, k)) // 如果超了,就去掉
                    b[i] = false;
                else    // 没有超就++
                    ++ans;
            }
        
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值