问题描述
给定一个整数数组和一个正整数x,任务是找出所有连续子数组,这些子数组元素乘积的末尾零的数量至少为x。末尾零的数量由乘积中因数2和5的最小对数决定。
问题描述
- 输入:
- 一个整数数组
a。 - 一个正整数
x,表示所需的最小末尾零数。
- 一个整数数组
- 输出:
- 返回数组
a中满足条件的连续子数组的数量,即这些子数组中元素乘积的末尾零的数量至少为x。
- 返回数组
方法思路
-
分解因子:
- 对于数组中的每个元素,分别计算其包含的2和5因子的数量。这一步骤通过不断地将元素除以2(直到不能整除)来计算2的因子数量,然后对剩余部分做同样的操作来计算5的因子数量。
-
前缀和累积:
- 构建两个数组
sum2和sum5,其中sum2[i]表示从数组开始到第i个位置为止2因子的总和,sum5[i]同理但针对5因子。这样可以快速查询任意区间内2和5因子的总数。
- 构建两个数组
-
滑动窗口统计:
- 使用双指针技术(滑动窗口)来寻找满足条件的子数组。右指针
right用于扩展窗口,左指针left用于收缩窗口。当当前窗口内的2和5因子数量均达到或超过x时,记录当前窗口的大小,并尝试移动左指针以减小窗口,同时保持条件成立。这种方法确保了我们不会遗漏任何可能的子数组。
- 使用双指针技术(滑动窗口)来寻找满足条件的子数组。右指针
Java代码实现详解
public class ZeroCountSubarrays {
public static int countSubarrays(int[] a, int x) {
int n = a.length;
// 初始化前缀和数组
int[] sum2 = new int[n + 1];
int[] sum5 = new int[n + 1];
// 计算每个元素的2和5因子数量,并更新前缀和
for (int i = 1; i <= n; i++) {
int num = a[i - 1], cnt2 = 0, cnt5 = 0;
while (num % 2 == 0) { // 计算2因子
cnt2++;
num /= 2;
}
while (num % 5 == 0) { // 计算5因子
cnt5++;
num /= 5;
}
sum2[i] = sum2[i - 1] + cnt2; // 更新2因子前缀和
sum5[i] = sum5[i - 1] + cnt5; // 更新5因子前缀和
}
int left = 0, result = 0;
// 滑动窗口遍历
for (int right = 1; right <= n; right++) {
// 当窗口内的2和5因子数量都达到x时
while (left < right && (sum2[right] - sum2[left] >= x && sum5[right] - sum5[left] >= x)) {
// 累加结果
result += n - right + 1;
// 尝试缩小窗口
left++;
}
}
return result % 1000000007; // 返回结果并取模
}
public static void main(String[] args) {
System.out.println(countSubarrays(new int[]{5, 2, 3, 50, 4}, 2)); // 输出6
System.out.println(countSubarrays(new int[]{10, 5, 2, 1}, 3)); // 输出0
System.out.println(countSubarrays(new int[]{25, 4, 8}, 1)); // 输出2
}
}