【leetcode周赛】第三期

竞赛第三期

334场力扣周赛

本文只是节选公众号的中的一篇,我的公众号每日都会更新,欢迎参观
公众号算法每日一更

6369. 左右元素和的差值

力扣链接:https://leetcode.cn/problems/left-and-right-sum-differences/

题目描述:

给你一个下标从 0 开始的整数数组 nums ,请你找出一个下标从 0 开始的整数数组 answer ,其中:

  • answer.length == nums.length
  • answer[i] = |leftSum[i] - rightSum[i]|

其中:

  • leftSum[i] 是数组 nums 中下标 i 左侧元素之和。如果不存在对应的元素,leftSum[i] = 0
  • rightSum[i] 是数组 nums 中下标 i 右侧元素之和。如果不存在对应的元素,rightSum[i] = 0

返回数组 answer

示例 1:

输入:nums = [10,4,8,3]
输出:[15,1,11,22]
解释:数组 leftSum 为 [0,10,14,22] 且数组 rightSum 为 [15,11,3,0] 。
数组 answer 为 [|0 - 15|,|10 - 11|,|14 - 3|,|22 - 0|] = [15,1,11,22] 。

示例 2:

输入:nums = [1]
输出:[0]
解释:数组 leftSum 为 [0] 且数组 rightSum 为 [0] 。
数组 answer 为 [|0 - 0|] = [0] 。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 105
  • 比赛解法:模拟

先算leftSum和rightSum

再求abs

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
// 当时忘了abs,就直接写了一个
int func(int a, int b){
    if(a > b)
        return a - b;
    else
        return b - a;
}

int* leftRigthDifference(int* nums, int numsSize, int* returnSize){
    *returnSize = numsSize;
    int* res = (int*)malloc(sizeof(int) * numsSize);
    int leftSum[numsSize], rightSum[numsSize];
    for(int i = 0; i < numsSize; i++){
        leftSum[i] = 0;
        rightSum[i] = 0;
    }
    for(int i = 1; i < numsSize; i++){
        int j = i;
        while(j){
            leftSum[i] += nums[--j];
        }
    }
    int k = 1;
    for(int i = numsSize - 2; i >= 0; i--){
        int j = k;
        while(j){
            rightSum[i] += nums[numsSize - j];
            j--;
        }
        k++;
    }
    for(int i = 0; i< numsSize; i++){
        res[i] = func(leftSum[i], rightSum[i]);

    }
    return res;
}
  • 大佬解法:数学思维
class Solution {
public:
    vector<int> leftRigthDifference(vector<int>& nums) {
        int n=nums.size(),i;
        vector<int> s(n+1),ans(n);
        // leftSum
        for(i=0;i<n;i++)s[i+1]=s[i]+nums[i];
        // 下面讲
        for(i=0;i<n;i++)ans[i]=abs(s[i]+s[i+1]-s[n]);
        return ans;
    }
};

nums = { x 1 x_1 x1, x 2 x_2 x2, x 3 x_3 x3, x 4 x_4 x4},则leftsum = { 0 0 0, x 1 x_1 x1, x 1 + x 2 x_1 + x_2 x1+x2, x 1 + x 2 + x 3 x_1 + x_2 + x_3 x1+x2+x3}, rightSum = { x 2 + x 3 + x 4 x_2 + x_3 +x_4 x2+x3+x4, x 3 + x 4 x_3 + x_4 x3+x4, x 4 x_4 x4, 0 0 0},不难得到,rightSum[i] = sum - leftSum[i + 1] ,故ans[i] = abs(leftSum[i] + leftSum[i+1] - sum);

小想法

当时20min模拟出来的,如果我用这个思路,估计5min能写出来,想到这个思路估计要10min,所以说数学思维还是很重要的!

6368. 找出字符串的可整除数组

力扣链接:https://leetcode.cn/problems/find-the-divisibility-array-of-a-string/

题目描述:

给你一个下标从 0 开始的字符串 word ,长度为 n ,由从 09 的数字组成。另给你一个正整数 m

word可整除数组 div 是一个长度为 n 的整数数组,并满足:

  • 如果 word[0,...,i] 所表示的 数值 能被 m 整除,div[i] = 1
  • 否则,div[i] = 0

返回 word 的可整除数组。

示例 1:

输入:word = "998244353", m = 3
输出:[1,1,0,0,0,1,1,0,0]
解释:仅有 4 个前缀可以被 3 整除:"9"、"99"、"998244" 和 "9982443" 。

示例 2:

输入:word = "1010", m = 10
输出:[0,1,0,1]
解释:仅有 2 个前缀可以被 10 整除:"10" 和 "1010" 。

提示:

  • 1 <= n <= 105
  • word.length == n
  • word 由数字 09 组成
  • 1 <= m <= 109

比赛解法:数组转数字

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* divisibilityArray(char * word, int m, int* returnSize){
    *returnSize = 0;
    int* res = (int*)malloc(sizeof(int) * 100000);
    long long arr[100000];
    int len = 0;
    
    for(int i = 0; word[i]; i++){
        int tmp = word[i] - '0';
        long long sum = 0;
        if(len){
            sum = arr[len - 1] * 10;
        }
        arr[len++] = sum + tmp;
    }
    for(int i = 0; i < len; i++){
        if(arr[i] % m == 0){
            res[(*returnSize)++] = 1;
        }
        else
            res[(*returnSize)++] = 0;
    }
    return res;
}
  • 大佬解法:取余
# id:雪景式
class Solution:
    def divisibilityArray(self, word: str, m: int) -> List[int]:
        num = 0
        ans = []
        for i in word:
            num = 10*num + int(i)
            num %= m
            ans.append(1 if num == 0 else 0)
        return ans

思路(id:tom):

  1. N[i]word[0 ~ i]表示的值。
  2. n[i]word[i]表示的数。
  3. 不难得出 : N[i] = N[i - 1] * 10 + n[i]
  4. 在此假设 : N[i - 1] = p * m + q(即余数是q)
  5. 那么 : N[i] % m = (p * m * 10) % m + (q * 10 + n[i]) % m
  6. 其中 : (p * m * 10) % m必能整除, 因此只要看后半部分。
// 补一个c的
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* divisibilityArray(char * word, int m, int* returnSize){
    int* res = (int*)malloc(sizeof(int) * 100000);
    *returnSize = 0;
    long long q = 0;
    
    for(int i = 0; word[i]; i++){
        q = q * 10 + (word[i] - '0');
        q %= m;
        res[(*returnSize)++] = !(q % m);
    }
    return res;
}

小想法

对于大数处理问题:

  1. 加减乘 => 对两数组操作,注意进位问题
  2. 除 => 化为取余问题,理论依据就是大佬tom写的

6367. 求出最多标记下标

力扣链接:https://leetcode.cn/problems/find-the-maximum-number-of-marked-indices/

题目描述:

给你一个下标从 0 开始的整数数组 nums

一开始,所有下标都没有被标记。你可以执行以下操作任意次:

  • 选择两个 互不相同且未标记 的下标 ij ,满足 2 * nums[i] <= nums[j] ,标记下标 ij

请你执行上述操作任意次,返回 nums 中最多可以标记的下标数目。

示例 1:

输入:nums = [3,5,2,4]
输出:2
解释:第一次操作中,选择 i = 2 和 j = 1 ,操作可以执行的原因是 2 * nums[2] <= nums[1] ,标记下标 2 和 1 。
没有其他更多可执行的操作,所以答案为 2 。

示例 2:

输入:nums = [9,2,5,4]
输出:4
解释:第一次操作中,选择 i = 3 和 j = 0 ,操作可以执行的原因是 2 * nums[3] <= nums[0] ,标记下标 3 和 0 。
第二次操作中,选择 i = 1 和 j = 2 ,操作可以执行的原因是 2 * nums[1] <= nums[2] ,标记下标 1 和 2 。
没有其他更多可执行的操作,所以答案为 4 。

示例 3:

输入:nums = [7,6,8]
输出:0
解释:没有任何可以执行的操作,所以答案为 0 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • 比赛解法:快排+双层for

    标记的时候有问题

//其中cmp函数应写为:
int cmp(const void *a, const void *b)
{
    //return *(int*)a - *(int*)b; //由小到大排序
    return *(int *)b - *(int *)a; //由大到小排序
}
int maxNumOfMarkedIndices(int* nums, int numsSize){
    qsort(nums, numsSize, sizeof(int), cmp);
    int tag[numsSize];
    int res = 0;
    for(int i = 0; i < numsSize; i++)
        tag[i] = 0;
    
    for(int i = 0; i < numsSize; i++){
        if(tag[i] == 1) continue;
        for(int j = i + 1; j < numsSize; j++){
            if(tag[j] == 1) continue;
            // 防止溢出,也可从小向大排序,写成2 * nums[i] <= nums[j]
            if(nums[i] / 2 >= nums[j]){
                tag[i] = 1;
                tag[j] = 1;
                res += 2;
                break;
            }
        }
    }
    return res;
}
  • 大佬解法:贪心+双指针
//其中cmp函数应写为:
int cmp(const void *a, const void *b)
{
    //return *(int*)a - *(int*)b; //由小到大排序
    return *(int *)b - *(int *)a; //由大到小排序
}
int maxNumOfMarkedIndices(int* nums, int numsSize){
    qsort(nums, numsSize, sizeof(int), cmp);
    int res = 0;
    int i = 0, j = (numsSize + 1) >> 1;
    int mid = j - 1;
    while(i <= mid && j < numsSize){
        if(nums[i] / 2 >= nums[j]){
            i++;
            res += 2;
        }
        j++;
    }
    return res;
}

小想法

原来不用标记,只有把指针的范围限制好就行,被题目误导了:)

重点在于j的初值,如果是[3,6,8,12],3匹配12或1匹配6,return 2,但实际,3匹配8,6匹配12,return 4;就是说j指向后半部分。

6366. 在网格图中访问一个格子的最少时间

力扣链接:https://leetcode.cn/problems/minimum-time-to-visit-a-cell-in-a-grid/

后期更新说明

本以为本次可以突破一题,今天这场打完,感觉自己水平差的有点多。

目前打算:每周周赛第二天更新题解(算是题解吧),平时理应更新树的算法,不过这几场比赛打下来发现每场二三题都和双指针、二分查找、贪心算法有关,所以会插一部分二分查找(这个比较薄弱),至于贪心,争取上半年搞定,尽快突破一题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

算法小能手

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

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

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

打赏作者

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

抵扣说明:

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

余额充值