题目描述
小正在研究一个数组,并想要计算出其中的连续子数组的某种特性。给定一个整数数组,你需要编写一个函数来返回乘积末尾零的数量大于等于的连续子数组的数量。由于答案可能非常大,你需要将结果对 取模后再返回。
输入
输出
算法
双指针, 分解因子
思路
我们需要找到一个整数数组中所有乘积末尾零的数量大于等于 x 的连续子数组数量。为了达到这一目标,可以注意到以下几点:
- 末尾零的来源:一个整数乘积的末尾零数量,取决于该数的
2和5因子个数。因为每一对2和5的组合会生成一个末尾零。 - 因子数量的计算:因此,计算子数组乘积中
2和5因子的数量可以确定末尾零的数量,即min(2的个数, 5的个数)。
步骤
我们可以将问题分解为以下几个步骤:
-
预处理每个数的因子个数:
- 遍历数组
a,对每个元素a[i],计算其2和5因子的数量。 - 这可以通过除法来实现,不断将
a[i]除以2,记录它的因子次数,直到a[i]不再能被2整除。对于5因子同理。 - 将这些因子信息记录在一个辅助数组
v中,v[i].first表示a[i]中2因子的数量,v[i].second表示5因子的数量。
- 遍历数组
-
滑动窗口算法统计满足条件的子数组: 使用滑动窗口(或双指针)来快速统计满足条件的连续子数组数量。
- 用两个指针
l和r代表滑动窗口的左右边界,r为窗口的右边界,l为左边界。 q和p分别表示窗口中所有元素的2和5因子总和。- 对于每个
r的位置,更新窗口中的因子数量q和p,将v[r].first和v[r].second分别加到q和p中。
- 用两个指针
-
满足条件的窗口处理:
- 判断当前窗口是否满足
min(q, p) >= x(即窗口内的2和5因子的最小值是否大于等于x)。 - 如果满足条件,则说明从
l到r的子数组满足条件,并且从l开始到r结束的所有连续子数组都会满足条件(这是因为r之后的每个数也会包含足够的2和5因子)。 - 所以当窗口满足条件时,可以直接将
a.size() - r个子数组计入满足条件的子数组数量中。 - 然后将
l向右移动,逐步减少窗口内2和5因子的数量,直到窗口不再满足条件。
- 判断当前窗口是否满足
-
返回答案:
- 滑动窗口遍历完成后,将计算出的满足条件的子数组数量
ans返回(结果需要对10^9 + 7取模)。
- 滑动窗口遍历完成后,将计算出的满足条件的子数组数量
复杂度分析
-
时间复杂度:
- 因子分解部分:遍历数组
a的每个元素,计算其2和5的因子数量。对每个数因子分解的复杂度为O(log a[i]),总复杂度为O(n log a[i])。 - 滑动窗口部分:指针
r每次右移一位,l根据条件动态调整,因此总时间复杂度为O(n)。 - 综合时间复杂度为
O(n log a[i])。
- 因子分解部分:遍历数组
-
空间复杂度:
- 辅助数组
v和其他常量空间需求,总体空间复杂度为O(n)。
- 辅助数组
代码分析
constexpr int mod = 1e9 + 7;
int solution(std::vector<int> a, int x) {
// Step 1: 预处理,计算每个元素的2和5因子数量
std::vector<std::pair<int, int> > v(a.size());
for (int i = 0; i < a.size(); i ++) {
int xx = a[i];
// 计算2因子数量
while (xx % 2 == 0) {
xx /= 2;
v[i].first++; // 记录2因子数量
}
// 计算5因子数量
while (xx % 5 == 0) {
xx /= 5;
v[i].second++; // 记录5因子数量
}
}
// Step 2: 滑动窗口,统计满足条件的子数组数量
int ans = 0;
int l = 0, r = 0;
int q = 0, p = 0; // q和p分别累计窗口内2和5因子的数量
while (r < a.size()) {
// 增加当前右边界的因子数量
q += v[r].first;
p += v[r].second;
// 如果当前窗口满足 min(q, p) >= x,则统计符合条件的子数组
while (std::min(q, p) >= x) {
ans = (ans + (a.size() - r)) % mod; // 统计满足条件的子数组数量
// 移动左边界,减少左端元素的因子数量
q -= v[l].first;
p -= v[l].second;
l++;
}
// 移动右边界
r++;
}
return ans;
}
难点分析总结
- 因子分解的逻辑:确保对每个数的
2和5因子数量统计准确。 - 滑动窗口的控制:要动态调整窗口边界,正确更新
q和p的值,使得min(q, p) >= x成立时统计子数组数量。 - 符合条件的子数组数量:满足条件时可以一次性将
(a.size() - r)个子数组计入答案,避免重复计算。