0%

第281场周赛解答

第281场周赛

链接↓

第281场周赛

小tips:由于国内服务器502崩溃,因此该周赛时长加长了10分钟

2180.统计各位数字之和为偶数的整数个数

给你一个正整数 num ,请你统计并返回 小于或等于 num 且各位数字之和为 偶数 的正整数的数目。

正整数的 各位数字之和 是其所有位上的对应数字相加的结果。

示例 1:

1
2
3
4
输入:num = 4
输出:2
解释:
只有 2 和 4 满足小于等于 4 且各位数字之和为偶数。

示例 2:

1
2
3
4
5
输入:num = 30
输出:14
解释:
只有 14 个整数满足小于等于 30 且各位数字之和为偶数,分别是:
2、4、6、8、11、13、15、17、19、20、22、24、26 和 28 。

提示:

  • 1 <= num <= 1000

小彩蛋:本题在比赛过程中示例二的解释出现了严重错误,只有 14 个整数满足小于等于 30中的30误打成了4


分析:

暴力枚举,按题目意思来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
int countEven(int num) {
int ans=0;
for(int i=1;i<=num;i++){
int a=i/10%10;
int b=i/100%100;
int c=i/1000%1000;
if((i+a+b+c)%2==0){
ans++;
}
}
return ans;
}
};

2181.合并零之间的节点

给你一个链表的头节点 head ,该链表包含由 0 分隔开的一连串整数。链表的 开端末尾 的节点都满足 Node.val == 0

对于每两个相邻的 0 ,请你将它们之间的所有节点合并成一个节点,其值是所有已合并节点的值之和。然后将所有 0 移除,修改后的链表不应该含有任何 0

返回修改后链表的头节点 head

示例 1:

img

1
2
3
4
5
6
输入:head = [0,3,1,0,4,5,2,0]
输出:[4,11]
解释:
上图表示输入的链表。修改后的链表包含:
- 标记为绿色的节点之和:3 + 1 = 4
- 标记为红色的节点之和:4 + 5 + 2 = 11

示例 2:

img

1
2
3
4
5
6
7
输入:head = [0,1,0,3,0,2,2,0]
输出:[1,3,4]
解释:
上图表示输入的链表。修改后的链表包含:
- 标记为绿色的节点之和:1 = 1
- 标记为红色的节点之和:3 = 3
- 标记为黄色的节点之和:2 + 2 = 4

提示:

  • 列表中的节点数目在范围 [3, 2 * 105]
  • 0 <= Node.val <= 1000
  • 存在连续两个 Node.val == 0 的节点
  • 链表的 开端末尾 节点都满足 Node.val == 0

分析:

遍历一次链表,设立一个容器,从第一个0到下一个为0中间的数求和,若中间没有数直接过,结点即都在num内。

记ret为头指针,cur为尾指针,通过循环vector的数字,让尾指针指向新节点并且把尾指针移过去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeNodes(ListNode* head) {
head=head->next;
int sum=0;
vector<int> num;
while(head!=NULL){
if(head->val==0){
num.push_back(sum);
sum=0;
}
else{
sum+=head->val;
}
head=head->next;
}

ListNode *ret,*cur;
ret=cur=new ListNode(num[0]);
int m=num.size();
for(int i=1;i<m;i++){
ListNode *node = new ListNode(num[i]);
cur->next=node;
cur=cur->next;
}
return ret;
}
};

2182.构造限制重复的字符串

给你一个字符串 s 和一个整数 repeatLimit ,用 s 中的字符构造一个新字符串 repeatLimitedString ,使任何字母 连续 出现的次数都不超过 repeatLimit 次。你不必使用 s 中的全部字符。

返回 字典序最大的 repeatLimitedString

如果在字符串 ab 不同的第一个位置,字符串 a 中的字母在字母表中出现时间比字符串 b 对应的字母晚,则认为字符串 a 比字符串 b 字典序更大 。如果字符串中前 min(a.length, b.length) 个字符都相同,那么较长的字符串字典序更大。

示例 1:

1
2
3
4
5
6
7
8
9
输入:s = "cczazcc", repeatLimit = 3
输出:"zzcccac"
解释:使用 s 中的所有字符来构造 repeatLimitedString "zzcccac"。
字母 'a' 连续出现至多 1 次。
字母 'c' 连续出现至多 3 次。
字母 'z' 连续出现至多 2 次。
因此,没有字母连续出现超过 repeatLimit 次,字符串是一个有效的 repeatLimitedString 。
该字符串是字典序最大的 repeatLimitedString ,所以返回 "zzcccac" 。
注意,尽管 "zzcccca" 字典序更大,但字母 'c' 连续出现超过 3 次,所以它不是一个有效的 repeatLimitedString 。

示例 2:

1
2
3
4
5
6
7
8
9
输入:s = "aababab", repeatLimit = 2
输出:"bbabaa"
解释:
使用 s 中的一些字符来构造 repeatLimitedString "bbabaa"。
字母 'a' 连续出现至多 2 次。
字母 'b' 连续出现至多 2 次。
因此,没有字母连续出现超过 repeatLimit 次,字符串是一个有效的 repeatLimitedString 。
该字符串是字典序最大的 repeatLimitedString ,所以返回 "bbabaa" 。
注意,尽管 "bbabaaa" 字典序更大,但字母 'a' 连续出现超过 2 次,所以它不是一个有效的 repeatLimitedString 。

提示:

  • 1 <= repeatLimit <= s.length <= 105
  • s 由小写英文字母组成

分析:

对于本题我们遍历一遍s,记录一下每个字母出现的次数,设立一个答案和当前值last描述的是哪个字母。

通过从后往前遍历,字典序大的优先输出,如果c和当前输入的是同一个值,那么我们判断是否超过repeatLimit,写入ans,记录一次reps,cnt内的字母数量减掉一个;如果c和当前输入的不是一个值,那么我们写进ans,记录一下这个值,记录一次reps,cnt内的字母数量减掉一个,如果最后遍历完仍然没有字母,那么我们直接跳出循环即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int cnt[30];
class Solution {
public:
string repeatLimitedString(string s, int repeatLimit) {
memset(cnt,0,sizeof(cnt));
for(char c:s) cnt[c-'a']+=1;
string ans="";
char last='#';
int reps=0;
while(true){
bool flag=false;
for(int i=25;i>=0&& !flag;i--){
if(cnt[i]==0) continue;
char c=(char)(i+'a');
if(c==last){
if(reps+1<=repeatLimit){
ans+=c;
reps+=1;
flag=true;
cnt[i]-=1;
}
}else{
ans+=c;
last=c;
reps=1;
flag=true;
cnt[i]-=1;
}
}
if(!flag) break;
}
return ans;
}
};

2183.统计可以被 K 整除的下标对数目

给你一个下标从 0 开始、长度为 n 的整数数组 nums 和一个整数 k ,返回满足下述条件的下标对 (i, j) 的数目:

  • 0 <= i < j <= n - 1
  • nums[i] * nums[j] 能被 k 整除。

示例 1:

1
2
3
4
5
6
7
输入:nums = [1,2,3,4,5], k = 2
输出:7
解释:
共有 7 对下标的对应积可以被 2 整除:
(0, 1)、(0, 3)、(1, 2)、(1, 3)、(1, 4)、(2, 3) 和 (3, 4)
它们的积分别是 2、4、6、8、10、12 和 20 。
其他下标对,例如 (0, 2) 和 (2, 4) 的乘积分别是 3 和 15 ,都无法被 2 整除。

示例 2:

1
2
3
输入:nums = [1,2,3,4], k = 5
输出:0
解释:不存在对应积可以被 5 整除的下标对。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i], k <= 105

分析:

首先本题与第72场双周赛的第一题是一个题目,不同的是本题卡了时间复杂度,因此如果考虑暴力的话会有这个错误

↓↓↓

Line 7: Char 24: runtime error: signed integer overflow: 98382 * 23997 cannot be represented in type ‘int’ (solution.cpp) SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:16:24

首先我们用cnts记录一下数组中每个数字出现多少次,再用rcnts记录当前这个i和我们目标数字乘起来是k的倍数。

后面我们就可以统计答案了,对于每一种值来遍历,对下标i,去找另一个下表至少需要拥有什么样的因子,能使得被k整除,即为nv,ans加上

cnts[i]*rcnts[nv](乘积为k的倍数),最后除去自己乘自己的情况,即(i==j),我们剩下了(i>j)与(i<j)的情况,由于题目要求i<j,那么我们只需要对ans除2即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define LL long long
int Gcd(int a,int b){
if(b==0) return a;
return Gcd(b,a%b);
}

const int MAXN=1e5+55;
LL cnts[MAXN];
LL rcnts[MAXN];

class Solution {
public:
long long countPairs(vector<int>& nums, int k) {
int n=nums.size(),mx=0;
memset(cnts,0,sizeof(cnts));
for(int i=0;i<n;i++) nums[i]=Gcd(nums[i],k);
for(int i=0;i<n;i++) cnts[nums[i]]+=1,mx=max(mx,nums[i]);

memset(rcnts,0,sizeof(rcnts));
for(int i=1;i<=mx;i++)
for(int j=i;j<=mx;j+=i) rcnts[i]+=cnts[j];

LL ans=0;
for(int i=0;i<=mx;i++){
if(cnts[i]==0) continue;
int nv=k/Gcd(k,i);
ans=ans+cnts[i]*rcnts[nv];
if(1LL*i*i%k==0) ans-=cnts[i];
}
return ans/2LL;
}
};