【LeetCode】第72场双周赛参赛实录

时间:2022-02-19 22:30-24:00

地址:竞赛 - 力扣 (LeetCode)

结局:本次依然是做了三道。第一题想法比较多,花了不少时间。所以排名比起之前还有所下降,不过可能年后参赛的人也多了一些。本次整体应该说难度是偏低的,但第四题属于基本没有思路的范畴。还需继续努力。

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(n^{2})的时间复杂度(做题的时候以为是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(n^{2})挨个遍历,可以省下空间,同时也省下很多编码的时间,不容易出错。

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 <= 10^{15}

思路:这题第一反应是不是有点简单过头了,很容易想到只要整除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 <= 10^{10}

思路:思考了两三分钟后,还是有思路的,可以简单想到,从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 <= 10^{5}
0 <= nums1[i], nums2[i] <= n - 1
nums1 和 nums2 是 [0, 1, ..., n - 1] 的排列。

思路:这题着实难住了,如果强行求解,是一个O(n^{3}),结合题中10^{5}的数据量,肯定是搞不定的。当时想了约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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值