2300. 咒语和药水的成功对数 - 力扣(LeetCode)
题目
给你两个正整数数组 spells
和 potions
,长度分别为 n
和 m
,其中 spells[i]
表示第 i
个咒语的能量强度,potions[j]
表示第 j
瓶药水的能量强度。
同时给你一个整数 success
。一个咒语和药水的能量强度 相乘 如果 大于等于 success
,那么它们视为一对 成功 的组合。
请你返回一个长度为 n
的整数数组 pairs
,其中 pairs[i]
是能跟第 i
个咒语成功组合的 药水 数目。
示例 1:
输入:spells = [5,1,3], potions = [1,2,3,4,5], success = 7
输出:[4,0,3]
解释:
- 第 0 个咒语:5 * [1,2,3,4,5] = [5,10,15,20,25] 。总共 4 个成功组合。
- 第 1 个咒语:1 * [1,2,3,4,5] = [1,2,3,4,5] 。总共 0 个成功组合。
- 第 2 个咒语:3 * [1,2,3,4,5] = [3,6,9,12,15] 。总共 3 个成功组合。
所以返回 [4,0,3] 。
示例 2:
输入:spells = [3,1,2], potions = [8,5,8], success = 16
输出:[2,0,2]
解释:
- 第 0 个咒语:3 * [8,5,8] = [24,15,24] 。总共 2 个成功组合。
- 第 1 个咒语:1 * [8,5,8] = [8,5,8] 。总共 0 个成功组合。
- 第 2 个咒语:2 * [8,5,8] = [16,10,16] 。总共 2 个成功组合。
所以返回 [2,0,2] 。
提示:
-
n == spells.length
-
m == potions.length
-
1 <= n, m <= 105
-
1 <= spells[i], potions[i] <= 105
-
1 <= success <= 1010
思路
- 因为返回的元素下标是要和咒语对应的,所以咒语数组不能改变,但是为了加速搜索速度,我们可以排序药水数组,然后通过二分找到最小的满足的药水,其后的药水都能满足,那么我们就得到了该咒语匹配的药水数量了。
- 循环n次匹配n个咒语之后即可。
- 这里二分查找的判断目标是可以固定的,通过success/spells[i] + ((sucecess%spells[i]==0)?0:1)来确定最小的potion的值。
代码实现
class Solution {
public:
vector<int> successfulPairs(vector<int>& spells, vector<int>& potions, long long success) {
vector<int> ans;
int pn = potions.size();
sort(potions.begin(), potions.end());
for(int i = 0; i < spells.size(); ++i) {
int left = 0, right = pn;
long long effect = success/spells[i] + ((success%spells[i]==0)?0:1);
while(left < right) {
int mid = left + (right-left) / 2;
if(potions[mid] >= effect) right = mid;
else left = mid + 1;
}
ans.emplace_back(pn-right);
}
return ans;
}
};
复杂度分析
- 时间复杂度:排序的时间复杂度是O(mlogm)的,遍历咒语数组的时间复杂度是O(n)的,内部二分查找的时间复杂度是O(logn)的,所以总的时间复杂度为O((m+n)logm)。
- 空间复杂度:O(n)/O(1)。
题解
- 官解的思路基本跟我差不多,另一种双指针的方法在同样时间复杂度下空间开销更大了,就不看了。
- 相对的,另一个大佬的解法就成功实现了空间换时间,将复杂度降到了O(n),学习一下:
- 首先,遍历potions数组找到其中的最大值,然后根据这个最大值建桶(如果空间复杂度有限制的话这个方法可能就不行了),然后在遍历一遍数组将元素填入桶内,最后再将每个桶的可行解个数倒序叠加。
- 那么,遍历咒语数组的过程中,就只用确定是不是小于等于最大的potion值,如果是,直接去对应的桶里面提个数,否则为0。
- 这样就实现了时间复杂度为O(n+m),空间复杂度为O(maxPotions)的代码了。
- 复现:
-
class Solution { public: vector<int> successfulPairs(vector<int>& spells, vector<int>& potions, long long success) { int m = potions.size(), maxValue = 0, n = spells.size(); vector<int> ans(n); for(int i = 0; i < m; ++i) maxValue = max(maxValue, potions[i]); vector<int> cnt(maxValue+2); for(int i = 0; i < m; ++i) cnt[potions[i]]++; for(int i = maxValue; i >= 1; --i) cnt[i] += cnt[i+1]; for(int i = 0; i < n; ++i) { long long effect = success/spells[i] + (success%spells[i]==0?0:1); if(effect <= maxValue) ans[i] = cnt[effect]; } return ans; } };