小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 = 10,k = 3 - 输出:
6 - 解释:最长的连续自然数乘积最多包含3个不同素因子的子数组是
[5, 6, 7, 8, 9, 10]。
- 输入:
-
样例2:
- 输入:
n = 20,k = 5 - 输出:
12 - 解释:最长的连续自然数乘积最多包含5个不同素因子的子数组是
[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]。
- 输入:
-
样例3:
- 输入:
n = 100,k = 4 - 输出:
10 - 解释:最长的连续自然数乘积最多包含4个不同素因子的子数组是
[13, 14, 15, 16, 17, 18, 19, 20, 21, 22]。
- 输入:
解题思路
滑动窗口与素数筛法
在这个问题中,我们需要找到一个连续的自然数子数组,使得这些数的乘积最多包含 k 个不同的素因子。具体思路如下:
-
素数筛法:
- 使用埃拉托斯特尼筛法(Sieve of Eratosthenes)生成
[1, n]范围内的所有素数。 - 为每个数生成其素因子列表。
- 使用埃拉托斯特尼筛法(Sieve of Eratosthenes)生成
-
滑动窗口:
- 使用滑动窗口技术,维护一个窗口
[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),我们使用了数组和哈希表来存储中间结果。
总结
通过使用素数筛法和滑动窗口技术,我们能够高效地解决这个问题,找到可以取的连续自然数的最大长度。这种方法不仅简洁,而且在实际应用中也非常实用。希望这篇文章能帮助你更好地理解和掌握这一技巧。如果你有任何问题或建议,欢迎在评论区留言讨论!