竞赛第三期
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
,由从0
到9
的数字组成。另给你一个正整数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
由数字0
到9
组成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):
- 记
N[i]
为word[0 ~ i]
表示的值。- 记
n[i]
为word[i]
表示的数。- 不难得出 :
N[i] = N[i - 1] * 10 + n[i]
- 在此假设 :
N[i - 1] = p * m + q
(即余数是q
)- 那么 :
N[i] % m = (p * m * 10) % m + (q * 10 + n[i]) % m
- 其中 :
(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;
}
小想法
对于大数处理问题:
- 加减乘 => 对两数组操作,注意进位问题
- 除 => 化为取余问题,理论依据就是大佬
tom
写的
6367. 求出最多标记下标
力扣链接:https://leetcode.cn/problems/find-the-maximum-number-of-marked-indices/
题目描述:
给你一个下标从 0 开始的整数数组
nums
。一开始,所有下标都没有被标记。你可以执行以下操作任意次:
- 选择两个 互不相同且未标记 的下标
i
和j
,满足2 * nums[i] <= nums[j]
,标记下标i
和j
。请你执行上述操作任意次,返回
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/
跳
后期更新说明
本以为本次可以突破一题,今天这场打完,感觉自己水平差的有点多。
目前打算:每周周赛第二天更新题解(算是题解吧),平时理应更新树的算法,不过这几场比赛打下来发现每场二三题都和双指针、二分查找、贪心算法有关,所以会插一部分二分查找(这个比较薄弱),至于贪心,争取上半年搞定,尽快突破一题。