0%

第86场双周赛-376名-4题

1661012250432

本期总结:#

  1. 涉及边界问题一定要自己检查看是否写错了,花1分钟检查总好过避免WA浪费5分钟

  2. 取余问题一定要注意负数的情况,必须要用加上Mod值直到大于0才能去取余

  3. 区间合并问题直接上并查集,别整更新左区间,更新右区间这种玩意,很容易错。


6156. 得到 K 个黑块的最少涂色次数 - 力扣(LeetCode)

很简单,滑动窗口,而且因为数量够小,直接暴力解决即可,不用做滑动窗口的特殊处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int minimumRecolors(String blocks, int k) {
int min = Integer.MAX_VALUE;
for (int i = 0; i < blocks.length();i++) {
int count = 0;
int j = 0;
for (j = i; j < i+k && j < blocks.length();j++) {
if (blocks.charAt(j) == 'W') {
count++;
}
}
if (j != i+k) {
continue;
}
min = Math.min(count, min);
}
return min;
}
}

6157. 二进制字符串重新安排顺序需要的时间 - 力扣(LeetCode)

1661012329860

1661012381790

首先花了几分钟,推断出来最多变化n次(因为每处理一次至少前缀1会增加1个)

那么直接暴力O(n2)即可

但是因为边界处理写急了导致出错,我应该自己提交前再检查一下的

下面是错误的地方:

1661012470616

i+1==s.length()了,怎么可能添加11, 最多添加1个1才对。

正确答案:

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
class Solution {
public int secondsToRemoveOccurrences(String s) {

int res = 0;
while (true) {
StringBuilder sb = new StringBuilder();
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '0' && (i+1!=s.length() && s.charAt(i + 1) == '1')) {
sb.append("10");
i++;
count++;
} else if (s.charAt(i) == '0' && (i+1 == s.length() || s.charAt(i + 1) == '0')) {
sb.append("0");
} else if (s.charAt(i) == '1' && (i+1 != s.length() && s.charAt(i + 1) == '1')) {
sb.append("11");
i++;
} else {
sb.append("1");
}
}
s = sb.toString();
if (count == 0) {
return res;
}
res++;
}
}
}

6158. 字母移位 II - 力扣(LeetCode)

1661012520347

1661012525774

做过这类题,和那种任务流的题目类似

直接记录每个位置有多少起点,多少终点,根据情况进行加或者减。

但是对于移位的处理有点问题

1
2
3
4
5
for (int i = 0; i < s.length();i++) {
count += startPlaces[i];
cs[i] = (char)((s.charAt(i) -'a' + count)%26 + 'a');
count += endPlaces[i];
}

看似很对, 却忘记了 负数取余也是负数。。

对负数取余应当通过加上26的倍数让他大于0才对。于是改成下面这样得到移位后的字符:

1
2
3
4
5
6
7
8
9
char getMod(char c, int count) {
int num = c - 'a' + count;
if (num < 0) {
int mul = num / 26;
num += (mul+2)*26;
}
num %= 26;
return (char)(num + 'a');
}

结果还是错

因为我忘记了num是负数的时候,num/26也是负数。。应该要加一个Math.abs

正确做法如下getMod:

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
class Solution {
char getMod(char c, int count) {
int num = c - 'a' + count;
if (num < 0) {
int mul = Math.abs(num / 26);
num += (mul+1)*26;
}
num %= 26;
return (char)(num + 'a');
}

public String shiftingLetters(String s, int[][] shifts) {
int[] startPlaces = new int[s.length()+1];
int[] endPlaces = new int[s.length()+1];
for (int i = 0; i < shifts.length;i++) {
int[] shift = shifts[i];
startPlaces[shift[0]] += (shift[2] == 1 ? 1: -1);
endPlaces[shift[1]] += (shift[2] == 0 ? 1: -1);
}

int count = 0;
char[] cs = new char[s.length()];
for (int i = 0; i < s.length();i++) {
count += startPlaces[i];
cs[i] = getMod(s.charAt(i), count);
count += endPlaces[i];
}
return new String(cs);
}

}

6159. 删除操作后的最大子段和 - 力扣(LeetCode)

1661012916961

1661012929643

这题又是做过类似的题,删点的题目可以反过来变成加点。

即从后往前逐步遍历,新增,逐步合并区间

结果还是用了错误的方式去更新,想着直接更新左右两个索引的sum值即可

实际上你要更新的应该是左边索引的最左点, 右边索引的最右点才对,而且每次合并都要修改左右的最左点或者最右点,非常麻烦,如下中间那4个if, 绕来绕去,注定要搞一次WA才能知道问题,非常不对劲:

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
class Solution {
public long[] maximumSegmentSum(int[] nums, int[] removeQueries) {
long[] sums = new long[nums.length];
long[] results = new long[nums.length];
int[] lefts = new int[nums.length];
int[] rights = new int[nums.length];
long maxNum = Long.MIN_VALUE;
for (int i = removeQueries.length-1;i>=1;i--) {
int index = removeQueries[i];
long leftSum = index-1 >= 0 ? sums[index-1] :0;
long rightSum = index + 1 < nums.length ? sums[index+1] : 0;

long sum = leftSum + rightSum + nums[index];
lefts[index] = rights[index] = index;
sums[index] = sum;

if (leftSum > 0) {
lefts[index] = lefts[index-1];
}
if (rightSum > 0) {
rights[index] = rights[index+1];
}
if (leftSum > 0) {
rights[lefts[index]] = rights[index];
sums[lefts[index]] = sum;
}
if (rightSum > 0) {
lefts[rights[index]] = lefts[index];
sums[rights[index]] = sum;
}
maxNum = Math.max(maxNum, sum);
results[i-1] = maxNum;
}
return results;
}
}

这种合并的题目就别取巧了, 直接并查集走起

注意并查集合并时,涉及val的合并, 在getParent中不需要更新val

在union中,注意顺序,p[p1]] = p2的话,说明p1的父亲变成了p2,那么应该是p2作为被加方,即sum[p2] += sum[p1],反过来了

另外注意要先取变量,不要直接基于函数去搞,下面这样是错误的!因为第一步之后已经发生变成了:

1
2
3
4
void union(int n1, int n2) {
parent[getParent(n1)] = getParent(n2);
sums[getParent(n2)] += sums[getParent(n1)];
}

应该是:

1
2
3
4
5
6
void union(int n1, int n2) {
int p1 = getParent(n1);
int p2 = getParent(n2);
parent[p1] = p2;
sums[p2] += sums[p1];
}

并查集正确做法:

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
39
40
41
42
43
44
45
46
47
48
class Solution {
long[] sums;
int[] parent;
public long[] maximumSegmentSum(int[] nums, int[] removeQueries) {
long[] results = new long[nums.length];
sums = new long[nums.length];
long maxNum = Long.MIN_VALUE;
parent = new int[nums.length];
for (int i = 0; i < parent.length;i++) {
parent[i] = i;
}
for (int i = removeQueries.length-1;i>=1;i--) {
int index = removeQueries[i];

long leftSum = index-1 >= 0 ? sums[getParent(index-1)] :0;
long rightSum = index + 1 < nums.length ? sums[getParent(index+1)] : 0;

sums[index] = nums[index];
if (leftSum > 0) {
union(index-1, index);
}
if (rightSum > 0) {
union(index, index+1);
}
maxNum = Math.max(maxNum, sums[getParent(index)]);
results[i-1] = maxNum;
}
return results;
}

void union(int n1, int n2) {
int p1 = getParent(n1);
int p2 = getParent(n2);
parent[p1] = p2;
sums[p2] += sums[p1];
}

int getParent(int num) {
int p = parent[num];
if (p == num) {
return p;
} else {
p = getParent(p);
}
parent[num] = p;
return p;
}
}