题目解析 连续子数组零尾数问题| 豆包MarsCode AI 刷题

82 阅读3分钟

题目传送门

连续子数组零尾数问题 - MarsCode

题意简述

小F正在研究一个整数数组,并希望计算出其中的连续子数组的某种特性。具体来说,给定一个整数数组,你需要编写一个函数来返回乘积末尾零的数量大于等于 x 的连续子数组的数量。由于答案可能非常大,结果需要对 10^9+7 取模后返回。

解题思路

  • 末尾零的计算:

  • 一个数的末尾零的数量由其因数中2和5的数量决定。具体来说,乘积的末尾零的数量等于其因数中2和5的最小数量。

  • 因此,我们需要计算每个子数组中2和5的因数数量,并找出它们的最小值。

2. 前缀和数组:

  • 为了快速计算任意子数组的2和5因数的数量,我们使用前缀和数组 count2 和 count5。

  • count2[i] 表示从数组开始到第 i-1 个元素中2的因数总数,count5[i] 类似。

  • 通过前缀和数组,我们可以在常数时间内计算任意子数组的2和5因数的数量。

  • 双重循环遍历子数组:

  • 通过双重循环遍历所有可能的子数组,计算每个子数组的2和5因数的数量差值,进而计算出末尾零的数量。

  • 对于每个子数组,计算 total2 = count2[end + 1] - count2[start] 和 total5 = count5[end + 1] - count5[start]。

  • 末尾零的数量为 Math.min(total2, total5)。

  • 结果计数:

  • 如果某个子数组的末尾零数量大于等于 x,则计数加一。

  • 由于结果可能非常大,计数时需要对 10^9+7 取模。

代码实现

public class Main {
    private static final int MOD = 1000000007;

    public static int solution(int[] a, int x) {
        int n = a.length;
        int[] count2 = new int[n + 1];
        int[] count5 = new int[n + 1];

        // 计算前缀和数组
        for (int i = 0; i < n; i++) {
            count2[i + 1] = count2[i] + countFactors(a[i], 2);
            count5[i + 1] = count5[i] + countFactors(a[i], 5);
        }

        int result = 0;

        // 遍历所有子数组
        for (int start = 0; start < n; start++) {
            for (int end = start; end < n; end++) {
                int total2 = count2[end + 1] - count2[start];
                int total5 = count5[end + 1] - count5[start];
                int zeros = Math.min(total2, total5);

                if (zeros >= x) {
                    result = (result + 1) % MOD;
                }
            }
        }

        return result;
    }

    // 计算一个数中某个因数的数量
    private static int countFactors(int num, int factor) {
        int count = 0;
        while (num % factor == 0 && num > 0) {
            count++;
            num /= factor;
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(solution(new int[]{5, 2, 3, 50, 4}, 2) == 6);
        System.out.println(solution(new int[]{10, 5, 2, 1}, 3) == 0);
        System.out.println(solution(new int[]{25, 4, 8}, 1) == 2);
    }
}

时间复杂度分析

该算法的时间复杂度为 𝑂(𝑛^2),其中 𝑛 是数组的长度。主要的时间消耗在于双重循环遍历所有可能的子数组。虽然时间复杂度较高,但对于较小的数组仍然是可行的。

用到的算法和数据结构

  • 前缀和数组:用于快速计算任意子数组的2和5因数的数量。

  • 双重循环:用于遍历所有可能的子数组。

详细分析

在实现过程中,我们首先通过 countFactors 函数计算每个元素中2和5的因数数量,并构建前缀和数组 count2 和 count5。接下来,通过双重循环遍历所有可能的子数组,计算每个子数组的2和5因数的数量差值,进而计算出末尾零的数量。最后,统计末尾零数量大于等于 x 的子数组数量,并对结果取模。这种方法通过前缀和数组的使用,能够在常数时间内计算任意子数组的2和5因数的数量,从而提高了效率。尽管双重循环的时间复杂度较高,但对于中小规模的数据集,该方法仍然是可行的。