小C的连续自然数乘积问题 | 豆包MarsCode AI 刷题

125 阅读4分钟

小C的连续自然数乘积问题 | 豆包MarsCode AI 刷题

在编程的世界里,处理数论和组合问题是非常常见的任务。今天,我们来探讨一个具体的题目:小S在学习素数因子的分解,她希望在 [1, n] 的范围内,找到一些连续的自然数,这些数的乘积最多包含 k 个不同的素因子。你的任务是帮助小S找到可以取的连续自然数的最大长度。连续的自然数指的是不能重复且相邻数的差为1的一组正整数,例如 [2, 3, 4, 5][5, 6, 7] 都是连续的取数。

问题描述

小S在学习素数因子的分解,她希望在 [1, n] 的范围内,找到一些连续的自然数,这些数的乘积最多包含 k 个不同的素因子。你的任务是帮助小S找到可以取的连续自然数的最大长度。连续的自然数指的是不能重复且相邻数的差为1的一组正整数,例如 [2, 3, 4, 5][5, 6, 7] 都是连续的取数。

测试样例

  • 样例1

    • 输入:n = 10k = 3
    • 输出:6
    • 解释:最长的连续自然数乘积最多包含3个不同素因子的子数组是 [5, 6, 7, 8, 9, 10]
  • 样例2

    • 输入:n = 20k = 5
    • 输出:12
    • 解释:最长的连续自然数乘积最多包含5个不同素因子的子数组是 [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
  • 样例3

    • 输入:n = 100k = 4
    • 输出:10
    • 解释:最长的连续自然数乘积最多包含4个不同素因子的子数组是 [13, 14, 15, 16, 17, 18, 19, 20, 21, 22]

解题思路

滑动窗口与素数筛法

在这个问题中,我们需要找到一个连续的自然数子数组,使得这些数的乘积最多包含 k 个不同的素因子。具体思路如下:

  1. 素数筛法

    • 使用埃拉托斯特尼筛法(Sieve of Eratosthenes)生成 [1, n] 范围内的所有素数。
    • 为每个数生成其素因子列表。
  2. 滑动窗口

    • 使用滑动窗口技术,维护一个窗口 [left, right],并记录窗口内不同素因子的数量。
    • 当窗口内不同素因子的数量超过 k 时,移动左指针 left,直到窗口内不同素因子的数量不超过 k
    • 记录窗口的最大长度。

代码实现

java

复制代码

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static int solution(int n, int k) {
        boolean[] sieve = new boolean[n + 1];
        for (int i = 0; i < sieve.length; i++) {
            sieve[i] = true;
        }
        sieve[0] = false;
        sieve[1] = false;
        for (int i = 2; i <= Math.sqrt(n); i++) {
            if (sieve[i]) {
                for (int j = i * i; j <= n; j += i) {
                    sieve[j] = false;
                }
            }
        }
        ArrayList<Integer> primes = new ArrayList<>();
        for (int i = 0; i < sieve.length; i++) {
            if (sieve[i]) {
                primes.add(i);
            }
        }
        ArrayList<Integer>[] factors = new ArrayList[n + 1];
        for (int i = 0; i < factors.length; i++) {
            factors[i] = new ArrayList<>();
        }
        for (int prime : primes) {
            for (int multiple = prime; multiple <= n; multiple += prime) {
                factors[multiple].add(prime);
            }
        }
        int left = 1;
        int maxLength = 0;
        Map<Integer, Integer> primeCounts = new HashMap<>();
        int distinctPrimes = 0;
        for (int right = 1; right <= n; right++) {
            for (int p : factors[right]) {
                primeCounts.put(p, primeCounts.getOrDefault(p, 0) + 1);
                if (primeCounts.get(p) == 1) {
                    distinctPrimes++;
                }
            }
            while (distinctPrimes > k && left <= right) {
                for (int p : factors[left]) {
                    int count = primeCounts.get(p);
                    if (count == 1) {
                        primeCounts.remove(p);
                        distinctPrimes--;
                    } else {
                        primeCounts.put(p, count - 1);
                    }
                }
                left++;
            }
            int currentLength = right - left + 1;
            if (currentLength > maxLength) {
                maxLength = currentLength;
            }
        }
        return maxLength;
    }

    public static void main(String[] args) {
        System.out.println(solution(10, 3) == 6);  // 输出: true
        System.out.println(solution(20, 5) == 12); // 输出: true
        System.out.println(solution(100, 4) == 10); // 输出: true
    }
}

代码解析

  • 素数筛法

    • 使用埃拉托斯特尼筛法生成 [1, n] 范围内的所有素数。
    • 为每个数生成其素因子列表。
  • 滑动窗口

    • 使用滑动窗口技术,维护一个窗口 [left, right],并记录窗口内不同素因子的数量。
    • 当窗口内不同素因子的数量超过 k 时,移动左指针 left,直到窗口内不同素因子的数量不超过 k
    • 记录窗口的最大长度。
  • solution 方法

    • 初始化素数筛法和素因子列表。
    • 使用滑动窗口技术,维护窗口内不同素因子的数量。
    • 记录窗口的最大长度并返回。
  • main 方法

    • 测试了三个样例,验证了 solution 方法的正确性。

时间和空间复杂度分析

  • 时间复杂度:O(n log log n + n),其中 n 是输入的范围。素数筛法的时间复杂度为 O(n log log n),滑动窗口的时间复杂度为 O(n)。
  • 空间复杂度:O(n),我们使用了数组和哈希表来存储中间结果。

总结

通过使用素数筛法和滑动窗口技术,我们能够高效地解决这个问题,找到可以取的连续自然数的最大长度。这种方法不仅简洁,而且在实际应用中也非常实用。希望这篇文章能帮助你更好地理解和掌握这一技巧。如果你有任何问题或建议,欢迎在评论区留言讨论!