连续子数组零尾数问题

114 阅读2分钟

问题描述

给定一个整数数组和一个正整数x,任务是找出所有连续子数组,这些子数组元素乘积的末尾零的数量至少为x。末尾零的数量由乘积中因数2和5的最小对数决定。


问题描述

  • 输入
    • 一个整数数组 a
    • 一个正整数 x,表示所需的最小末尾零数。
  • 输出
    • 返回数组 a 中满足条件的连续子数组的数量,即这些子数组中元素乘积的末尾零的数量至少为 x

方法思路

  1. 分解因子

    • 对于数组中的每个元素,分别计算其包含的2和5因子的数量。这一步骤通过不断地将元素除以2(直到不能整除)来计算2的因子数量,然后对剩余部分做同样的操作来计算5的因子数量。
  2. 前缀和累积

    • 构建两个数组sum2sum5,其中sum2[i]表示从数组开始到第i个位置为止2因子的总和,sum5[i]同理但针对5因子。这样可以快速查询任意区间内2和5因子的总数。
  3. 滑动窗口统计

    • 使用双指针技术(滑动窗口)来寻找满足条件的子数组。右指针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
    }
}