刷题的日常-咒语和药水的成功对数

215 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

刷题的日常

一天一题,保持脑子清爽

咒语和药水的成功对数

该题来自周赛中的第二题,中等题,题意如:

给你两个正整数数组 spells 和 potions ,长度分别为 n 和 m ,其中 spells[i] 表示第 i 个咒语的能量强度,potions[j] 表示第 j 瓶药水的能量强度。
同时给你一个整数 success 。一个咒语和药水的能量强度 相乘 如果 大于等于 success ,那么它们视为一对 成功 的组合。
请你返回一个长度为 n 的整数数组 pairs,其中 pairs[i] 是能跟第 i 个咒语成功组合的 药水 数目。 其中:

  • n == spells.length
  • m == potions.length
  • 1 <= n, m <= 105
  • 1 <= spells[i], potions[i] <= 105
  • 1 <= success <= 1010

理解题意

由题意我们可以将条件提取出来,如下:

  • 给定两个数组 spells 和 potions,分别从两数组中各取出一个数,如果这两个数的乘积大于 给定的 success值,就算是一堆 成功 的组合
  • 统计以上所有数对的个数并返回
  • 给定的数组不一定是有序的,题目不做保证

做题思路

这里可以有两种解法:暴力,或者是二分。

暴力解

很简单,就是两层for循环

  • 将两个数组的数进行两两相乘
  • 将乘积出来的值和 success 比对,满足条件将结果加一,最终返回结果 比较简单,代码就不提供了。

排序 + 二分查找

我们知道,如果数组是有序的话,并且某个值的乘积 大于 success,那么这个值之后的数的乘积肯定也是大于success的,后面的数就没必要再次匹配,直接将往后的值加上即可。

  • 先将 potions 进行排序
  • 遍历 spells 数组
  • 在遍历的过程中,进行二分查找,找出在potions中使二者乘积大于success的索引
  • 如果结果存在,就用数组长度减去当前找到的索引
  • 最终将统计结果返回 因为排序需要O(mlogm),遍历 spells 每个数的过程中进行二分,所以需要O(nlogm),总时间复杂度为O(nlogn)。

代码实现

代码也不难,实现如下:

public class Solution {

    public int[] successfulPairs(int[] spells, int[] potions, long success) {
        int[] result = new int[spells.length];
        Arrays.sort(potions);
        for (int i = 0; i < spells.length; i++) {
            int spell = spells[i];
            int index = findFirst(potions, spell, success);
            if (index == -1) {
                continue;
            }
            result[i] = potions.length - index;
        }
        return result;
    }

    private int findFirst(int[] potions, long spell, long success) {
        int pre = 0, post = potions.length - 1, mid;
        while (pre < post) {
            mid = pre + (post - pre) / 2;
            if (spell * potions[mid] >= success) {
                post = mid;
            } else {
                pre = mid + 1;
            }
        }
        return potions[pre] * spell >= success ? pre : -1;
    }

}