leetcode 72期双周赛

145 阅读4分钟

笔者平时有做leetcode周赛的习惯,目前稳定可以a三题,偶尔运气好可以全a,以后会多多分享周赛题解,一起进步一起交流。

统计数组中相等且可以被整除的数对

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k ,请你返回满足 0 <= i < j < n ,nums[i] == nums[j] 且 (i * j) 能被 k 整除的数对 (i, j) 的 数目 。

输入: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 整除。

解题思路:只需要知道哪个数在哪个位置上存在,当遍历到这个值的时候与前面出现过的这个数的索引位置进行乘积看看是不是k的倍数就好。简单的演绎法即可解决。

class Solution {
    public int countPairs(int[] nums, int k) {
        Map<Integer, List<Integer>> mark = new HashMap<Integer, List<Integer>>();
        int res = 0;
        for(int i = 0; i < nums.length; i++) {
            if(mark.get(nums[i]) == null) {
                List<Integer> list = new ArrayList<Integer>();
                list.add(i);
                mark.put(nums[i],list);
            } else {
                List<Integer> list = mark.get(nums[i]);
                for(int j = 0; j < list.size(); j ++) {
                    if((list.get(j) * i) % k == 0) res++;
                }
                list.add(i);
            }
        }
        return res;
    }
}

找到和为给定整数的三个连续整数

给你一个整数 num ,请你返回三个连续的整数,它们的 num 。如果 num 无法被表示成三个连续整数的和,请你返回一个 数组。

解题思路: 假设这三个连续的自然数是n, n + 1, n + 2, 对于本题,有n + n + 1 + n + 2 = 3n + 3 = 3(n + 1) = num,也就是说num是这三个自然数中中间的那个数的三倍。所以,num必须是3的倍数,如果不满足这个情况,就无法变成3个连续整数的和。以下是解题代码。

class Solution {
    public long[] sumOfThree(long num) {
        if(num % 3 != 0) return new long[]{};
        return new long[]{num/3 - 1, num/3, num/3 + 1};
    }
}

拆分成最多数目的偶整数之和

给你一个整数 finalSum 。请你将它拆分成若干个 互不相同 的偶整数之和,且拆分出来的偶整数数目 最多 。

比方说,给你 finalSum = 12 ,那么这些拆分是 符合要求 的(互不相同的偶整数且和为 finalSum):(2 + 10) ,(2 + 4 + 6) 和 (4 + 8) 。它们中,(2 + 4 + 6) 包含最多数目的整数。注意 finalSum 不能拆分成 (2 + 2 + 4 + 4) ,因为拆分出来的整数必须互不相同。 请你返回一个整数数组,表示将整数拆分成 最多 数目的偶整数数组。如果没有办法将 finalSum 进行拆分,请你返回一个 空 数组。你可以按 任意 顺序返回这些整数。

输入: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] 等等也都是可行的解。

解题思路:这里用到了贪心的策略。

  1. 首先我们知道 偶数 + 偶数 = 偶数,换言之,如果一个数等于若干个偶数的和,那么这个数就是偶数。所以如果这个数是奇数就不会存在偶数组合相加可以得到这个奇数。
  2. 要追求追多,那么每次用当前没用过的最小的偶数去抵消totalSum就好。比如用了2,下一次就用4。
  3. 值得注意的是,在使用贪心策略的时候,我们要保证totalSum剩下的数,要大于base * 2, 这是为什么呢?因为不能使用相同的偶数,所以在做totalSum递减的时候,将totalSum = base + 一个大于base的偶数,也就是说把剩下的finalSum分成base和一个大于base的偶数,如果剩下接下来拆分不下去了,将这个大于base的偶数添加进结果就好,这样就保证了结果里的偶数是不重复的。
class Solution {
    public List<Long> maximumEvenSplit(long finalSum) {
        if(finalSum % 2 == 1) return new ArrayList<Long>();
        List<Long> res = new ArrayList<Long>();
        long base = 2;
        while(finalSum > 2 * base) {
            res.add(base);
            finalSum -= base;
            base += 2;
        }
        if(finalSum != 0) res.add(finalSum);
        return res;
    }
}

统计数组中好三元组数目

给你两个下标从 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) 。

请你返回好三元组的 总数目 。

输入: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) 。

解题思路:存在0 <= x, y, z <= n - 1 ,且 pos1x < pos1y < pos1z 和 pos2x < pos2y < pos2z 都成立的 (x, y, z),那么我们以中间的数作为基准,我们只需要知道中间这个数有几个相同的数left,和这个数右边有多少个相同的数right,那么以这个数作为中间数的三元组这个时候就存在left * right,我们将数组上每个元素的left和right统计出来相乘求和就好。

import java.util.Arrays;
​
class Solution {
    int[] change(int[] nums1, int[] nums2) {
        int[] a = new int[nums1.length];
        for (int i = 0;i < nums1.length;i++) {
            a[nums1[i]] = i;
        }
        int[] ret = new int[nums1.length];
        for (int i = 0;i < nums1.length;i++) {
            ret[i] = a[nums2[i]];
        }
        return ret;
    }
​
    void merge(int[] data, int left, int right, int[] countLeft, int[] countRight, int[] temp) {
        if (left == right) {
            return;
        }
        int mid = (left + right) / 2;
        merge(data, left, mid, countLeft, countRight, temp);
        merge(data, mid + 1, right, countLeft, countRight, temp);
​
        int i = left;
        int j = mid + 1;
        int now = left;
        while (i <= mid || j <= right) {
            if (i > mid) {
                temp[now++] = data[j];
                countLeft[data[j]] += i - left;
                j++;
            } else if (j > right) {
                temp[now++] = data[i];
                countRight[data[i]] += right - j + 1;
                i++;
            } else if (data[i] < data[j]) {
                temp[now++] = data[i];
                countRight[data[i]] += right - j + 1;
                i++;
            } else {
                temp[now++] = data[j];
                countLeft[data[j]] += i - left;
                j++;
            }
        }
        for (i = left;i <= right;i++) {
            data[i] = temp[i];
        }
    }
​
    public long goodTriplets(int[] nums1, int[] nums2) {
        int[] data = change(nums1, nums2);
        int[] countLeft = new int[nums1.length];
        int[] countRight = new int[nums2.length];
        int[] temp = new int[nums1.length];
        merge(data, 0, data.length - 1, countLeft, countRight, temp);
        long ret = 0;
        for (int i = 0;i < countLeft.length;i++) {
            ret += (long)countLeft[i] * countRight[i];
        }
        return ret;
    }
}

个人推广

以下是笔者公众号,欢迎多多关注,一起成长。

image.png