时间:2022-02-19 22:30-24:00
结局:本次依然是做了三道。第一题想法比较多,花了不少时间。所以排名比起之前还有所下降,不过可能年后参赛的人也多了一些。本次整体应该说难度是偏低的,但第四题属于基本没有思路的范畴。还需继续努力。
5996. 统计数组中相等且可以被整除的数对
给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k ,请你返回满足 0 <= i < j < n ,nums[i] == nums[j] 且 (i * j) 能被 k 整除的数对 (i, j) 的 数目 。
示例 1:
输入:nums = [3,1,2,2,2,1,3], k = 2
输出:4
解释:
总共有 4 对数符合所有要求:
- nums[0] == nums[6] 且 0 * 6 == 0 ,能被 2 整除。
- nums[2] == nums[3] 且 2 * 3 == 6 ,能被 2 整除。
- nums[2] == nums[4] 且 2 * 4 == 8 ,能被 2 整除。
- nums[3] == nums[4] 且 3 * 4 == 12 ,能被 2 整除。
示例 2:输入:nums = [1,2,3,4], k = 1
输出:0
解释:由于数组中没有重复数值,所以没有数对 (i,j) 符合所有要求。提示:
1 <= nums.length <= 100
1 <= nums[i], k <= 100
通过次数4,117提交次数5,034
思路:这题第一次是把题目理解错了的,要注意描述。再次理解后,很快想到了使用hash来记录相同值的位置的思路。再遍历列表,理论上应是O()的时间复杂度(做题的时候以为是O(n))。
public int countPairs(int[] nums, int k) {
HashMap<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
List<Integer> list = map.get(nums[i]);
list.add(i);
} else {
List<Integer> list = new ArrayList<>();
list.add(i);
map.put(nums[i], list);
}
}
int res = 0;
for (Map.Entry entry : map.entrySet()) {
List<Integer> list = (List<Integer>)entry.getValue();
if (list.size() > 1) {
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if ((list.get(i) * list.get(j)) % k == 0) {
res++;
}
}
}
}
}
return res;
}
后续整理:事实上,这个效率,可能还不如直接O()挨个遍历,可以省下空间,同时也省下很多编码的时间,不容易出错。
public int countPairs(int[] nums, int k) {
int count = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
count += nums[i] != nums[j] || i * j % k > 0 ? 0 : 1;
}
}
return count;
}
5997. 找到和为给定整数的三个连续整数
给你一个整数 num ,请你返回三个连续的整数,它们的 和 为 num 。如果 num 无法被表示成三个连续整数的和,请你返回一个 空 数组。
示例 1:
输入:num = 33
输出:[10,11,12]
解释:33 可以表示为 10 + 11 + 12 = 33 。
10, 11, 12 是 3 个连续整数,所以返回 [10, 11, 12] 。
示例 2:输入:num = 4
输出:[]
解释:没有办法将 4 表示成 3 个连续整数的和。
提示:
0 <= num <=
思路:这题第一反应是不是有点简单过头了,很容易想到只要整除3就行,而且这三个数也很容易拿到。
public long[] sumOfThree(long num) {
if (num % 3 != 0) {
return new long[0];
}
long[] res = new long[]{num / 3 - 1, num / 3, num / 3 + 1};
return res;
}
后续整理:没搞明白为什么这个难度会是第二题,稀里糊涂的就做了,整理的时候也没看到什么说法。
5998. 拆分成最多数目的偶整数之和
给你一个整数 finalSum 。请你将它拆分成若干个 互不相同 的偶整数之和,且拆分出来的偶整数数目 最多 。
比方说,给你 finalSum = 12 ,那么这些拆分是 符合要求 的(互不相同的偶整数且和为 finalSum):(2 + 10) ,(2 + 4 + 6) 和 (4 + 8) 。它们中,(2 + 4 + 6) 包含最多数目的整数。注意 finalSum 不能拆分成 (2 + 2 + 4 + 4) ,因为拆分出来的整数必须互不相同。
请你返回一个整数数组,表示将整数拆分成 最多 数目的偶整数数组。如果没有办法将 finalSum 进行拆分,请你返回一个 空 数组。你可以按 任意 顺序返回这些整数。示例 1:
输入:finalSum = 12
输出:[2,4,6]
解释:以下是一些符合要求的拆分:(2 + 10),(2 + 4 + 6) 和 (4 + 8) 。
(2 + 4 + 6) 为最多数目的整数,数目为 3 ,所以我们返回 [2,4,6] 。
[2,6,4] ,[6,2,4] 等等也都是可行的解。
示例 2:输入:finalSum = 7
输出:[]
解释:没有办法将 finalSum 进行拆分。
所以返回空数组。
示例 3:输入:finalSum = 28
输出:[6,8,2,12]
解释:以下是一些符合要求的拆分:(2 + 26),(6 + 8 + 2 + 12) 和 (4 + 24) 。
(6 + 8 + 2 + 12) 有最多数目的整数,数目为 4 ,所以我们返回 [6,8,2,12] 。
[10,2,4,12] ,[6,2,4,16] 等等也都是可行的解。
提示:
1 <= finalSum <=
思路:思考了两三分钟后,还是有思路的,可以简单想到,从2、4、6开始算起,最后一个数如果超了,补到前一个数就行了,类似于贪心。例如28,从2、4、6、8、10来做,到10的时候已经超过28了,把超出的部分补到8,构成2、4、6、16即可。这个数学上当时没心思去证明,只是从感觉上来说是没问题的,就上了。
public List<Long> maximumEvenSplit(long finalSum) {
if (finalSum % 2 != 0) {
return new ArrayList<>();
}
List<Long> list = new ArrayList<>();
long k = 1;
while (finalSum > 0) {
finalSum = finalSum - k * 2;
if (finalSum < 0) {
break;
}
list.add(k * 2);
k++;
}
if (finalSum < 0) {
long v = list.get(list.size() - 1);
v = v + finalSum + (k * 2);
list.set(list.size() - 1, v);
}
return list;
}
后续整理:应该说这个思路还是主流了,写法还可以更简洁,这就不去纠结了。
5999. 统计数组中好三元组数目
给你两个下标从 0 开始且长度为 n 的整数数组 nums1 和 nums2 ,两者都是 [0, 1, ..., n - 1] 的 排列 。
好三元组 指的是 3 个 互不相同 的值,且它们在数组 nums1 和 nums2 中出现顺序保持一致。换句话说,如果我们将 pos1v 记为值 v 在 nums1 中出现的位置,pos2v 为值 v 在 nums2 中的位置,那么一个好三元组定义为 0 <= x, y, z <= n - 1 ,且 pos1x < pos1y < pos1z 和 pos2x < pos2y < pos2z 都成立的 (x, y, z) 。
请你返回好三元组的 总数目 。
示例 1:
输入:nums1 = [2,0,1,3], nums2 = [0,1,2,3]
输出:1
解释:
总共有 4 个三元组 (x,y,z) 满足 pos1x < pos1y < pos1z ,分别是 (2,0,1) ,(2,0,3) ,(2,1,3) 和 (0,1,3) 。
这些三元组中,只有 (0,1,3) 满足 pos2x < pos2y < pos2z 。所以只有 1 个好三元组。
示例 2:输入:nums1 = [4,0,1,3,2], nums2 = [4,1,0,2,3]
输出:4
解释:总共有 4 个好三元组 (4,0,3) ,(4,0,2) ,(4,1,3) 和 (4,1,2) 。提示:
n == nums1.length == nums2.length
3 <= n <=
0 <= nums1[i], nums2[i] <= n - 1
nums1 和 nums2 是 [0, 1, ..., n - 1] 的排列。
思路:这题着实难住了,如果强行求解,是一个O(),结合题中的数据量,肯定是搞不定的。当时想了约20分钟,就直接放弃了。
后续整理:直接找一个解答吧。应该说不完全是智商的问题,还是题做的不够,对一些数据结构不敏感,就很容易完全没有思路。
方法一:树状数组/线段树/平衡树
首先用哈希表记录每个数在数组二中的位置,然后按照数组一的顺序依次处理。我们考虑以当前数字作为三元组中间数字的好三元组的数目。第一个数字需要是之前已经遍历过的,并且在数组二中的位置比当前数字更靠前的;第三个数字需要是当前还没有遍历过的,并且在数组二中的位置比当前数字更靠后的。这里只对数字的位置有要求,而对数字具体的值没有要求。
如何快速求出满足条件的第一个数字和第三个数字的个数呢?
以 [4,0,1,3,2][4,1,0,2,3]为例,考虑我们的遍历过程:
首先处理的是 4,此时数组二中的出现情况为:
[4,X,X,X,X]
我们需要统计的是 4 之前的有值的个数(0 个),以及 4 之后的没有值的个数(4 个)。因此以 4 为中间数字能形成 0 个好三元组。
接下来是 0,此时数组二中的出现情况为:
[4,X,0,X,X]
0 之前有值的个数(1 个),0 之后没有值的个数(2 个)。因此以 0 为中间数字能形成 2 个好三元组。
接下来是 1,此时数组二中的出现情况为:
[4,1,0,X,X]
1 之前有值的个数(1 个),1 之后没有值的个数(2 个)。因此以 1 为中间数字能形成 2 个好三元组。
接下来是 3,此时数组二中的出现情况为:
[4,1,0,X,3]
3 之前有值的个数(3 个),3 之后没有值的个数(0 个)。因此以 3 为中间数字能形成 0 个好三元组。
最后是 2,此时数组二中的出现情况为:
[4,1,0,2,3]
2 之前有值的个数(3 个),2 之后没有值的个数(0 个)。因此以 2 为中间数字能形成 0 个好三元组。
最后的答案是 4。
因为我们并不关心数字具体的值,而只关心是否出现过,所以我们实际上可以把数组二的出现情况用一个 0–1 数组来表示:
[1,0,0,0,0]→[1,0,1,0,0]→[1,1,1,0,0]→[1,1,1,0,1]→[1,1,1,1,1]
这时可以看出,我们用树状数组(或者线段树、平衡树)就能快速更新状态,并求出我们需要的两个数值(左边的 1 的个数和右边的 0 的个数)。
理清思路之后,代码的实现是比较容易的。
时间复杂度O(NlogN)。
空间复杂度O(N)。作者:lucifer1004
链接:https://leetcode-cn.com/problems/count-good-triplets-in-an-array/solution/shu-zhuang-shu-zu-xian-duan-shu-ping-hen-knho/