第315场周赛

第 315 场周赛 - 力扣(LeetCode)https://leetcode.cn/contest/weekly-contest-315/

6204. 与对应负数同时存在的最大正整数-Easy

题目描述:

给你一个 不包含 任何零的整数数组 nums ,找出自身与对应的负数都在数组中存在的最大正整数 k 。

返回正整数 k ,如果不存在这样的整数,返回 -1 。

题目解析:

直接两个嵌套的for循环进行查找,如果存在一个数的正负数都在数组中那么将该数存起来,并且在查找中更新最大值即可,代码如下:

class Solution {
public:
    int findMaxK(vector<int>& nums) {
        int res = -1;
        for(int i = 0 ; i<nums.size() ; i++){
            for(int j = 0 ; j<nums.size() ; j++){
                if(nums[j]==-nums[i]&&j!=i){
                    res = max(res,abs(nums[j]));
                }
            }
        }
        return res;
    }
};

执行用时:344 ms, 在所有 C++ 提交中击败了100.00%的用户

内存消耗:19.1 MB, 在所有 C++ 提交中击败了100.00%的用户


6205. 反转之后不同整数的数目-Medium

题目描述:

给你一个由 正 整数组成的数组 nums 。

你必须取出数组中的每个整数,反转其中每个数位,并将反转后得到的数字添加到数组的末尾。这一操作只针对 nums 中原有的整数执行。

返回结果数组中 不同 整数的数目。

题目解析:

首先通过一个函数计算每个数的反转数,将其存入数组中,然后对数组进行排序,遍历数组寻找数组有几个不同的值即可,其实感觉可以用unordered_set来解决这个问题,这里就不细说了,代码如下:

class Solution {
public:
    int cal(int num){
        int res = 0;
        while(num!=0){
            res = res*10+num%10;
            num/=10;
        }
        return res;
    }
    
    int countDistinctIntegers(vector<int>& nums) {
        int len = nums.size();
        for(int i = 0 ; i<len ; i++){
            int temp = cal(nums[i]);
            nums.push_back(temp);
        }
        sort(nums.begin(),nums.end());
        int res = 1;
        int temp = nums[0];
        for(int i = 1 ; i<nums.size() ; i++){
            if(nums[i]!=temp){
                temp = nums[i];
                res++;
            }
        }
        return res;
    }
};

执行用时:172 ms, 在所有 C++ 提交中击败了100.00%的用户

内存消耗:57.7 MB, 在所有 C++ 提交中击败了100.00%的用户


6219. 反转之后的数字和-Medium

题目描述:

给你一个 非负 整数 num 。如果存在某个 非负 整数 k 满足 k + reverse(k) = num  ,则返回 true ;否则,返回 false 。

reverse(k) 表示 k 反转每个数位后得到的数字。

题目解析:

暴力枚举,挺简单的,通过上一道题的计算反转数字的函数,然后进行枚举即可,代码如下:

class Solution {
public:
    int cal(int num){
        int res = 0;
        while(num!=0){
            res = res*10+num%10;
            num/=10;
        }
        return res;
    }
    
    bool sumOfNumberAndReverse(int num) {
        for(int i = 0 ; i<=num ; i++){
            if(i+cal(i)==num)return true;
        }
        return false;
    }
};

执行用时:80 ms, 在所有 C++ 提交中击败了100.00%的用户

内存消耗:5.9 MB, 在所有 C++ 提交中击败了100.00%的用户


6207. 统计定界子数组的数目-Hard

题目描述:

给你一个整数数组 nums 和两个整数 minK 以及 maxK 。

nums 的定界子数组是满足下述条件的一个子数组:

子数组中的 最小值 等于 minK 。
子数组中的 最大值 等于 maxK 。
返回定界子数组的数目。

子数组是数组中的一个连续部分。

题目解析:

有点子小难,主要是有点难想,前面三道题一共花了差不多二十多分钟,然后最后一道题发呆愣了一个小时没做出来,我一开始的想法是通过单调栈的原理来找,即我先找到一个minK-maxK或者maxK-minK的区间,然后我在这个区间上进行左右扩散,看最远能扩到多少,比如能够扩n个数,那么这个区间能够构成的子数组就是,1+2+...+n-1,但是这个做法就很难去做去重,所以我将近一个小时都在考虑怎么去重,代码如下(虽然是错的,但是个经验,该说不说,我还挺能写废话代码):

class Solution {
public:
    long long countSubarrays(vector<int>& nums, int minK, int maxK) {
        vector<int> num;
        vector<int> pos;
        stack<int> stk;
        long long ans = 0;
        for(int i = 0 ; i<nums.size() ; i++){
            if(stk.empty()&&(nums[i]==minK||nums[i]==maxK))stk.push(i);
            if(!stk.empty()&&(nums[i]==minK&&nums[stk.top()]==maxK)){
                int res = 1;
                int pos = 0;
                for(pos = stk.top()-1 ; pos>=0 ; pos--){
                    if(nums[pos]<=maxK&&nums[pos]>=minK)res++;
                    else break;
                }
                for(pos = i+1 ; pos<nums.size() ; pos++){
                    if(nums[pos]<=maxK&&nums[pos]>=minK)res++;
                    else break;
                }
                num.push_back(res);
                stk.pop();
                i = pos-1;
            }
            if(!stk.empty()&&(nums[i]==maxK&&nums[stk.top()]==minK)){
                int res = 1;
                int pos = 0;
                for(pos = stk.top()-1 ; pos>=0 ; pos--){
                    if(nums[pos]<=maxK&&nums[pos]>=minK)res++;
                    else break;
                }
                for(pos = i+1 ; pos<nums.size() ; pos++){
                    if(nums[pos]<=maxK&&nums[pos]>=minK)res++;
                    else break;
                }
                num.push_back(res);
                stk.pop();
                i = pos-1;
            }
        }
        
        stack<int> stk_c;
        for(int i = 0 ; i<nums.size() ; i++){
            if(stk_c.empty()&&(nums[i]==minK||nums[i]==maxK))stk_c.push(i);
            if(!stk_c.empty()&&(nums[i]==minK&&nums[stk_c.top()]==maxK)){
                ans++;
                stk_c.pop();
            }
            if(!stk_c.empty()&&(nums[i]==maxK&&nums[stk_c.top()]==minK)){
                ans++;
                stk_c.pop();
            }
        }
        for(int i = 0 ; i<num.size() ; i++){
            for(int data = num[i]-1 ; data>0 ; data--){
                ans += data;
            }
        }
        return ans;
    }
};

然后看了一下题解过后,恍然大悟,是真的聪明,题目是怎么考虑的呢,首先我在遍历的过程中记录最近一次出现的minK和maxK的位置,并且记录最近的一个不在minK和maxK范围内的位置,如果这个数可以作为子区间的右端点,那么这个数的左端点有多少选择呢,minK和maxK位置的最小值减去最近的超出范围的数据位置,如果小于0的话,说明以该数为右端点不能构成符合条件的子数组,则为0即可,代码如下:

class Solution {
public:
    long long countSubarrays(vector<int>& nums, int minK, int maxK) {
        long long res = 0;
        int minI = -1;
        int maxI = -1;
        int idx = -1;
        for(int i = 0 ; i<nums.size() ; i++){
            if(nums[i]==minK)minI = i;
            if(nums[i]==maxK)maxI = i;
            if(nums[i]<minK||nums[i]>maxK)idx = i;
            res += max(min(minI,maxI)-idx,0);
        }
        return res;
    }
};

执行用时:68 ms, 在所有 C++ 提交中击败了100.00%的用户

内存消耗:68.7 MB, 在所有 C++ 提交中击败了100.00%的用户

感觉就是这种题可以通过从后往前去考虑,而不是一直想着正向怎么去求解,我只需要计算每个数作为右端点能够产生几个子数组,那么所有数对应的结果加起来就是我要求的结果。算法的魅力~!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值