均值为u/v的子数组
在介绍这题前,先介绍lc 560 和为K的子数组。
和为K的连续子数组
链接:https://leetcode.cn/problems/subarray-sum-equals-k/description/
给你一个整数数组 nums
和一个整数 k
,请你统计并返回该数组中和为 k
的连续子数组的个数。
暴力解法
回溯法遍历每一个连续子数组,计算子数组的和并与K相比。时间复杂度$O(N^3)$ 。
这样做显然不行,有优化的地方吗?有。计算子数组的和。
前缀和优化
我们知道前缀和这一技巧,在做一次预处理后,计算某个区间的和只需要O(1)时间。那么题目转化为:求有多少个区间【l,r】和为K,其中l和r范围是0~N-1,且l<r。
具体的解题过程就变成了两层循环:
1 | for(int r=0; r<N; ++r){ |
能不能再优化?
哈希表优化
计算公式:$presum[j] - presum[i] = k$ ,那么 $presum[j] -k = presum[i]$
转换思路:考虑以$j$为右端点的区间,其区间和为K的个数。这等价于求多少个$i<j$,满足 $presum[i] = presum[j] -k $
等等,这个公式有点熟悉?力扣梦开始的地方,力扣第一题,两数之和。这里是两数之差。
接下来的思路就很明朗了,空间换时间,利用哈希宝提前存每个presum[i]。
1 | class Solution { |
细节注意:
- 以往前缀和下标都是不对应的,比如preSum[i+1] 为从0到i得区间和。
这道题的下标是对应的,原因是 map.put(0, 1);
- map.put(0, 1); 的作用
其实还是前缀和下标对应的问题。当我们要计算第一个区间,即[0,0]的区间和时,我们采用了preSum[0]-0的实现。
微众银行第三题-平均值
微众银行笔试/20230903/第三题赏析
题目详情
(1)题目描述
小明有一个数组。他挑选了一个有理数u/v,现在他想知道这个数组有多少个子区间的平均值恰好等于u/v。数组的子区间即是数组中连续的一段区间,如数组[4,2,6]有6个子区间 [4],[2],[6],[4,2],[2,6],[4,2,6]。
(2)输入描述
第一行有三个整数 n,u,v (1 ≤ n, v ≤ 100000,1 ≤ u ≤ n *v),代表数组的长度,小明选择的有理数的分子和分母。
输入保证u和v的最大公因数是1,即u/v是最简分数。
第二行有n个绝对值不超过1000000的整数,代表数组中的元素。
数字间两两有空格隔开。
(3)输出描述
输出一个非负整数,代表所求的答案。
(4)样例
输入
1 | 6 5 2 |
输出
1 | 6 |
思路分析
仔细思考,如果将题目描述的中平均值去除,这不就是和为K的子数组吗?
条件给的数组平均值与一个真分数,是想要我们干什么呢?回到判断相等的公式。
$\frac{presum[j]-presum[i]}{j-i]} = \frac{u}{v}$
我们将它变形一下:
$u\times i - v \times presum[i] = u \times j - v \times presum[j]$
等等,这两边的形式好像高度对称啊,我们定义一个新数组 $a[i] = u \times i - v \times presum[i]$,上面公式转化为了
$a[i] = a[j] - 0$
这不就是两数之差为k嘛,只不过k为0。兜兜转转,我们又回到了力扣第一题。
题解
1 | import java.util.Scanner; |