题解:连续子数组满足条件的数量
问题分析
1. 问题目标
给定一个整数数组,我们需要计算其中的连续子数组,使得这些子数组的乘积末尾的零的数量大于等于 (x)。
2. 问题核心
- 末尾零的数量:一个数的乘积末尾零的数量由因子 (2) 和 (5) 的对数决定,因为每对 (2 \times 5) 产生一个零。因此,问题可转化为:
- 对每个连续子数组,判断因子 (2) 和 (5) 的数量,是否满足 (\min(\text{因子} \ 2, \text{因子} \ 5) \geq x)。
3. 如何高效计算
- 滑动窗口:
- 使用滑动窗口技巧,以固定右端点,动态调整左端点,确保窗口内的子数组满足条件。
- 每次确定窗口后,直接统计以右端点为终点的所有合法子数组。
求解步骤
1. 辅助函数
- 预处理每个数字中包含的 (2) 和 (5) 的因子数量。通过不断除以 (2) 或 (5) 统计数量。
- 复杂度为 (O(\log(\text{num}))),实际运行效率较高。
2. 滑动窗口
- 使用两个指针 (left) 和 (right),表示当前的窗口区间。
- 对于每个右端点 (right),累积窗口内 (2) 和 (5) 的因子数量。
- 如果满足 (\min(\text{因子} \ 2, \text{因子} \ 5) \geq x),调整左端点 (left),并计算所有以 (right) 为终点的合法子数组数量。
3. 取模操作
- 因为结果可能很大,最终答案对 (10^9 + 7) 取模。
代码实现
以下是完整的 C++ 实现:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MOD = 1e9 + 7;
// 辅助函数:计算数字中某因子的数量
int count_factors(int n, int factor) {
int count = 0;
while (n > 0 && n % factor == 0) {
count++;
n /= factor;
}
return count;
}
// 主函数:计算满足条件的连续子数组数量
int solution(vector<int> a, int x) {
int n = a.size();
vector<int> fives(n, 0), twos(n, 0);
// 预处理:计算每个数中因子 2 和 5 的数量
for (int i = 0; i < n; i++) {
fives[i] = count_factors(a[i], 5);
twos[i] = count_factors(a[i], 2);
}
// 滑动窗口
int left = 0, result = 0;
int total_fives = 0, total_twos = 0;
for (int right = 0; right < n; right++) {
// 累加当前右端点的因子数量
total_fives += fives[right];
total_twos += twos[right];
// 移动左端点,确保窗口内满足条件
while (min(total_fives, total_twos) >= x) {
total_fives -= fives[left];
total_twos -= twos[left];
left++;
}
// 以当前 right 为终点的所有子数组均满足条件
result = (result + left) % MOD;
}
return result;
}
// 测试用例
int main() {
vector<int> a1 = {5, 2, 3, 50, 4};
cout << (solution(a1, 2) == 6) << endl; // 输出:6
vector<int> a2 = {10, 5, 2, 1};
cout << (solution(a2, 3) == 0) << endl; // 输出:0
vector<int> a3 = {25, 4, 8};
cout << (solution(a3, 1) == 2) << endl; // 输出:2
return 0;
}
代码解析
1. 预处理
- 遍历数组中的每个数字,计算其中 (2) 和 (5) 的因子数量。
- 使用 (O(n \cdot \log(\text{num}))) 时间完成。
2. 滑动窗口
- 动态维护 (total_fives) 和 (total_twos) 作为当前窗口内因子 (2) 和 (5) 的数量总和。
- 如果 (\min(total_fives, total_twos) \geq x),调整 (left) 确保满足条件。
3. 结果累加
- 以每个 (right) 为右端点的合法子数组数量为 (left)。
- 累加结果并对 (MOD = 10^9 + 7) 取模。
复杂度分析
-
时间复杂度
- 预处理因子:(O(n \cdot \log(\text{num})))。
- 滑动窗口:(O(n))。
- 总复杂度:(O(n \cdot \log(\text{num})))。
-
空间复杂度
- 额外数组存储因子:(O(n))。
- 总复杂度:(O(n))。
测试分析
示例 1
- 输入:(a = [5, 2, 3, 50, 4], x = 2)
- 分析:
- (5) 的因子数:([1, 0, 0, 2, 0])
- (2) 的因子数:([0, 1, 1, 0, 2])
- 滑动窗口结果:满足条件的子数组有 6 个。
- 输出:6
示例 2
- 输入:(a = [10, 5, 2, 1], x = 3)
- 分析:
- 乘积的末尾零不可能大于等于 3。
- 输出:0
示例 3
- 输入:(a = [25, 4, 8], x = 1)
- 分析:
- 滑动窗口统计满足条件的子数组数量为 2。
- 输出:2
总结
- 本题通过 滑动窗口 优化计算,保证效率。
- 核心在于因子分解和子数组统计,算法简单高效。
- 模块化设计便于理解和扩展,适合处理更复杂的数值问题。