1352-最后 K 个数的乘积
请你实现一个「数字乘积类」ProductOfNumbers
,要求支持下述两种方法:
1. add(int num)
- 将数字
num
添加到当前数字列表的最后面。
2. getProduct(int k)
- 返回当前数字列表中,最后
k
个数字的乘积。 - 你可以假设当前列表中始终 至少 包含
k
个数字。
题目数据保证:任何时候,任一连续数字序列的乘积都在 32-bit 整数范围内,不会溢出。
示例:
**输入:**
["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"]
[[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]
**输出:**
[null,null,null,null,null,null,20,40,0,null,32]
**解释:**
ProductOfNumbers productOfNumbers = new ProductOfNumbers();
productOfNumbers.add(3); // [3]
productOfNumbers.add(0); // [3,0]
productOfNumbers.add(2); // [3,0,2]
productOfNumbers.add(5); // [3,0,2,5]
productOfNumbers.add(4); // [3,0,2,5,4]
productOfNumbers.getProduct(2); // 返回 20 。最后 2 个数字的乘积是 5 * 4 = 20
productOfNumbers.getProduct(3); // 返回 40 。最后 3 个数字的乘积是 2 * 5 * 4 = 40
productOfNumbers.getProduct(4); // 返回 0 。最后 4 个数字的乘积是 0 * 2 * 5 * 4 = 0
productOfNumbers.add(8); // [3,0,2,5,4,8]
productOfNumbers.getProduct(2); // 返回 32 。最后 2 个数字的乘积是 4 * 8 = 32
提示:
add
和getProduct
两种操作加起来总共不会超过40000
次。0 <= num <= 100
1 <= k <= 40000
方法一:
如果没有 0 的话,直接考虑维护一个前缀积 pre[i] 表示前 i 个数的乘积即可,答案就是 pre[n]}{pre[n-k],其中 n 表示当前 pre 数组的长度。那么如何处理 0 呢?可以注意到如果出现 0 的话,那么 0 之前的数对答案都是没有用的了,所以我们可以遇到 0 的时候直接清空 pre 数组,那么询问的时候我们要求的是末尾 k 个数的乘积,如果这时候我们 pre 数组的长度小于 k,说明末尾 k 个数里肯定有 0,直接输出 0 即可,否则输出 pre[n]}{pre[n-k],简言之:
getProduct(k)=\left{\begin{matrix}
0, n< k\
pre[n]}{pre[n-k]},n\geq k
\end{matrix}\right.
,其中n=pre.length()。
1 | class ProductOfNumbers { |
复杂度分析
- 时间复杂度:add 和 getProduct 复杂度均为 O(1)。
- 空间复杂度:O(n),需要额外提供一个辅助数组。
方法二:
注意到题目说了一句话:题目数据保证:任何时候,任一连续数字序列的乘积都在 32-bit 整数范围内,不会溢出,这其实告诉了我们如果只乘大于 1 的数话,数字序列长度最多不会超过 32,因为大于 1 最小的数 2 的连着 32 个乘起来已经达到题目乘积的上界,所以我们只需要忽略 0 和 1,在查询的时候暴力乘复杂度就能得到保证。
开三个数组:
- vec[i]:存加入的数字 vec[i]
- cnt[i]:前 i 数里 0 的个数和,即 0 个数的前缀和
- pre[i]:[0..i-1] 最后一个非 0 和非 1 的位置
对于 add 操作:主要是 cnt 和 pre 数组怎么更新,由于 cnt 存的是 0 个数的前缀和,所以可以得到
cnt[n]=cnt[n-1]+(num==0)
对于 pre 数组,由定义也很容易得到转移式:
pre[n]=\left{\begin{matrix}
pre[n-1], vec[n-1]<=1\
n-1,vec[n-1]>1
\end{matrix}\right.
更新都是 O(1) 的。
对于 getProduct 操作:先通过 cnt 数组差分得到末尾 k 个数里 0 的个数,如果大于 0 直接返回 0 即可,否则就根据 pre[i] 从最后一个位置开始不断往前跳然后计算答案,直到跳过 k 个数结束,根据前面的性质分析我们会知道这个过程最多跳 32 次。
1 | class ProductOfNumbers { |
复杂度分析
- 时间复杂度:add 操作复杂度为 O(1) 和 getProduct 操作复杂度最坏情况为 O(log_2S),S 为值域的上界。
- 空间复杂度:O(n),需要额外提供三个辅助数组。