leetcode-第291场周赛

本文介绍了如何高效地移除指定数字以最大化结果,通过观察数字规律减少比较次数。接着,讨论了解决含有最多K个可整除元素子数组的问题,以及找到必须拿起的最小连续卡牌数。最后,探讨字符串引力的计算,包括子串引力的统计技巧。
摘要由CSDN通过智能技术生成

2259. 移除指定数字得到的最大结果

给你一个表示某个正整数的字符串 number和一个字符 digit

number中 恰好 移除 一个 等于 digit 的字符后,找出并返回按 十进制 表示 最大 的结果字符串。生成的测试用例满足 digitnumber中出现至少一次。

示例 1:

输入:number = “123”, digit = “3”
输出:“12”
解释:“123” 中只有一个 ‘3’ ,在移除 ‘3’ 之后,结果为 “12” 。

示例 2:

输入:number = “1231”, digit = “1”
输出:“231”
解释:可以移除第一个 ‘1’ 得到 “231” 或者移除第二个 ‘1’ 得到 “123” 。
由于 231 > 123 ,返回 “231” 。

示例 3:

输入:number = “551”, digit = “5”
输出:“51”
解释:可以从 “551” 中移除第一个或者第二个 ‘5’ 。
两种方案的结果都是 “51” 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-digit-from-number-to-maximize-result
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:

  1. 首先第一个想法,我挨个挨个试一次呗,我从头到尾遍历,只要是跟我digit相等的字符,我就把他去掉,然后把他保存,后面只要遇到我就把这个字符去掉然后跟之前保存的比较,最后谁最大我就返回谁

but!but!你有没有发现,这种效率不是很高,什么你问我为什么不是很高。很简单。
number = "12131415161",digit="1";如果我们暴力法一次比较。我们需要比较6次!但是我们发现没有?实际上我number无论去掉哪一个字符,最终都是number.length()-1位数。既然是数学上比较大小的问题,那说明什么?肯定是高位越大的同样长度的数字更大啊。你说我去掉第一个1。我最高位就是2了,如果我去掉后面的其他的1。我的高位肯定是是1啊。这部显而易见。肯定是去掉第一个1就行了。也就是我只需要操作一次就行了啊。如此一来我们的思路↓

  1. 从头往后遍历
  2. 只要遇到number.charAt(i) == digit我就看number.charAt(i+1)是否比number.charAt(i)大,如果大,越早去掉越好。否则的话,就去掉最后一个
  3. 有个小细节,我们最后一个字符可能不太好比较,所以我们可以给number加一个其他字符。最后返回的时候删除就行了

代码实现:

class Solution {
    public String removeDigit(String number, char digit) {
        number = number + "-";//为了最后一个字符也能比较
        int len = number.length();
        char[] ch = number.toCharArray();
        String temp = "";

        for (int i = 1; i < len; i++) {
            if (digit == ch[i - 1] && i != len) {
                temp = number.substring(0, i - 1) + number.substring(i, len);
                if (ch[i] > ch[i - 1]) {
                    break;
                }
            }
        }
        return temp.substring(0,len-2);//记得把最后一个字符去掉
    }
}

2260. 必须拿起的最小连续卡牌数

给你一个整数数组 cards ,其中 cards[i] 表示第 i 张卡牌的 值 。如果两张卡牌的值相同,则认为这一对卡牌 匹配 。

返回你必须拿起的最小连续卡牌数,以使在拿起的卡牌中有一对匹配的卡牌。如果无法得到一对匹配的卡牌,返回 -1 。

示例 1:

输入:cards = [3,4,2,3,4,7]
输出:4
解释:拿起卡牌 [3,4,2,3] 将会包含一对值为 3 的匹配卡牌。注意,拿起 [4,2,3,4] 也是最优方案。

示例 2:

输入:cards = [1,0,5,3]
输出:-1
解释:无法找出含一对匹配卡牌的一组连续卡牌。

思路:
没什么好说的,用HashMap的key不可重复的特性,保存和cards[i]相同值的前一个值的下标。那么我就知道当前这副牌有i - map.get(cards[i]) + 1张。从前往后不断迭代更新出最短的就行

代码实现:

class Solution {
    public int minimumCardPickup(int[] cards) {
        int len = cards.length;
        int res = Integer.MAX_VALUE;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < len; i++) {
            if (map.containsKey(cards[i])) {
                res = Math.min(res, 1 + i - map.get(cards[i]));
            }
            map.put(cards[i], i);
        }
        if (res == Integer.MAX_VALUE) {
            return -1;
        }
        return res;
    }
}

2261. 含最多 K 个可整除元素的子数组

给你一个整数数组 nums 和两个整数 k 和 p ,找出并返回满足要求的不同的子数组数,要求子数组中最多 k 个可被 p 整除的元素。

如果满足下述条件之一,则认为数组 nums1 和 nums2 是 不同 数组:

两数组长度 不同 ,或者
存在 至少 一个下标 i 满足 nums1[i] != nums2[i] 。
子数组 定义为:数组中的连续元素组成的一个 非空 序列。

示例 1:

输入:nums = [2,3,3,2,2], k = 2, p = 2
输出:11
解释:
位于下标 0、3 和 4 的元素都可以被 p = 2 整除。
共计 11 个不同子数组都满足最多含 k = 2 个可以被 2 整除的元素:
[2]、[2,3]、[2,3,3]、[2,3,3,2]、[3]、[3,3]、[3,3,2]、[3,3,2,2]、[3,2]、[3,2,2] 和 [2,2] 。
注意,尽管子数组 [2] 和 [3] 在 nums 中出现不止一次,但统计时只计数一次。
子数组 [2,3,3,2,2] 不满足条件,因为其中有 3 个元素可以被 2 整除。

示例 2:

输入:nums = [1,2,3,4], k = 4, p = 1
输出:10
解释:
nums 中的所有元素都可以被 p = 1 整除。
此外,nums 中的每个子数组都满足最多 4 个元素可以被 1 整除。
因为所有子数组互不相同,因此满足所有限制条件的子数组总数为 10 。

思路:

  1. 这不就是排列组合吗,但是这个排列组合是有顺序的,有顺序的排列组合
  2. 这个组合里有重复的数字,所以记得去重==>用Set数据结构

ps:为啥Set可以去重?那我就跟你好好battle一下了。Set的主要实现类是HashSet,他继承了Set接口。拥有Set接口众多的方法。HashSet底层是用HashMap的key作为他的value的。HashMap底层是Hash表,根据他的key的hash值作为下标,下标只有一个所以key不可重复。因此HashSet的value不能重复。你可能会问,我用HashMap的key作为value值,那HashMap的value用来干嘛呢?不好意思,这个value是个特殊值,一般没用。只有当HashSet加入一个重复值时,底层发现已经有该值了,会将value返回,表示加入的值重复了。

扯那么多其实这道题没啥捷径,从前往后遍历吧。如果有好方法,希望有人可以在评论告诉我
代码实现:

class Solution {
    public int countDistinct(int[] nums, int k, int p) {
        HashSet<String> ans = new HashSet<>();
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            int cnt = k;
            StringBuilder sb = new StringBuilder();
            for (int j = i; j < len; j++) {
                sb.append(nums[j]).append("+");
                if (nums[j] % p == 0) {
                    cnt--;
                }
                if (cnt < 0) {
                    break;
                }
                ans.add(sb.toString());
            }
        }
        return ans.size();
    }
}

6050. 字符串的总引力

字符串的 引力 定义为:字符串中 不同 字符的数量。

例如,“abbca” 的引力为 3 ,因为其中有 3 个不同字符 ‘a’、‘b’ 和 ‘c’ 。
给你一个字符串 s ,返回 其所有子字符串的总引力 。

子字符串 定义为:字符串中的一个连续字符序列。

示例 1:

输入:s = “abbca”
输出:28
解释:“abbca” 的子字符串有:

  • 长度为 1 的子字符串:“a”、“b”、“b”、“c”、“a” 的引力分别为 1、1、1、1、1,总和为 5 。
  • 长度为 2 的子字符串:“ab”、“bb”、“bc”、“ca” 的引力分别为 2、1、2、2 ,总和为 7 。
  • 长度为 3 的子字符串:“abb”、“bbc”、“bca” 的引力分别为 2、2、3 ,总和为 7 。
  • 长度为 4 的子字符串:“abbc”、“bbca” 的引力分别为 3、3 ,总和为 6 。
  • 长度为 5 的子字符串:“abbca” 的引力为 3 ,总和为 3 。
    引力总和为 5 + 7 + 7 + 6 + 3 = 28 。

示例 2:

输入:s = “code”
输出:20
解释:“code” 的子字符串有:

  • 长度为 1 的子字符串:“c”、“o”、“d”、“e” 的引力分别为 1、1、1、1 ,总和为 4 。
  • 长度为 2 的子字符串:“co”、“od”、“de” 的引力分别为 2、2、2 ,总和为 6 。
  • 长度为 3 的子字符串:“cod”、“ode” 的引力分别为 3、3 ,总和为 6 。
  • 长度为 4 的子字符串:“code” 的引力为 4 ,总和为 4 。
    引力总和为 4 + 6 + 6 + 4 = 20 。

思路:

  1. 将所有子串按照其末尾字符的下标分组。
  2. 考虑两组相邻的子串:以 s[i-1]s[i−1] 结尾的子串、以 s[i]s[i] 结尾的子串。
  3. 以 s[i]s[i] 结尾的子串,可以看成是以 s[i-1]s[i−1] 结尾的子串,在末尾添加上 s[i]s[i] 组成。

上面这一串提示是思考子串统计类问题的通用技巧之一。

  1. 从左往右遍历 ss,考虑将 s[i]s[i] 添加到以 s[i-1]s[i−1] 结尾的子串的末尾。添加后,这些子串的引力值会增加多少?
  2. 分类讨论:
  • 如果 s[i]s[i] 之前没有遇到过,那么这些子串的引力值都会增加 11,这些子串的引力值之和会增加 ii,再加上 11,即 s[i]s[i] 单独组成的子串的引力值;
  • 如果 s[i]s[i] 之前遇到过,设其上次出现的下标为 jj,那么向子串 s[0…i-1],\ s[1…i-1],\ s[2…i-1],\cdots,s[j…i-1]s[0…i−1], s[1…i−1], s[2…i−1],⋯,s[j…i−1] 的末尾添加 s[i]s[i] 后,这些子串的引力值是不会变化的,因为 s[i]s[i] 已经在 s[j]s[j] 处出现过了;而子串 s[j+1…i-1],\ s[j+2…i-1],\cdots,s[i-1…i-1]s[j+1…i−1], s[j+2…i−1],⋯,s[i−1…i−1] 由于不包含字符 s[i]s[i],这些子串的引力值都会增加 11,因此有 i-j-1i−j−1 个子串的引力值会增加 11,这些子串的引力值之和会增加 i-j-1i−j−1,再加上 11,即 s[i]s[i] 单独组成的子串的引力值。

代码实现:

class Solution {
    public long appealSum(String s) {
        int len = s.length();
        int[] pos = new int[26];
        Arrays.fill(pos, -1);
        long sum = 0;
        for (int i = 0; i < s.length(); i++) {
            int index = s.charAt(i) - 'a';
            sum += (i - pos[index]) * (len - i);
            pos[index] = i;
        }

        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

走出半生仍是少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值