Medium
题目描述
给定两个正整数 l 和 r。对于任意数字 x,除了 x 本身之外的所有正因数称为 x 的真因数。
如果一个数字恰好有 2 个真因数,则称为特殊数字。例如:
- 数字 4 是特殊数字,因为它有真因数 1 和 2。
- 数字 6 不是特殊数字,因为它有真因数 1、2 和 3。
返回在范围 [l, r] 内不是特殊数字的数字数量。
示例 1:
输入:l = 5, r = 7
输出:3
解释:在范围 [5, 7] 内没有特殊数字。
示例 2:
输入:l = 4, r = 16
输出:11
解释:在范围 [4, 16] 内的特殊数字是 4 和 9。
约束条件:
- 1 <= l <= r <= 10^9
解题思路
解题思路
首先需要理解特殊数字的定义:一个数恰好有2个真因数。
通过分析可以发现,一个数有恰好2个真因数当且仅当它是质数的平方。证明如下:
- 对于质数p的平方p²,其因数为:1, p, p²
- 真因数(除自身外的因数)为:1, p,恰好2个
- 对于其他数字,要么因数过少(如质数只有1个真因数),要么因数过多(如合数)
因此问题转化为:在[l, r]范围内,有多少个质数的平方?
算法步骤:
- 确定需要检查的质数范围:由于我们要找p²在[l, r]内的质数p,所以p的范围是[√l, √r]
- 使用埃拉托斯特尼筛法预处理出所有小于等于√(10^9) ≈ 31623的质数
- 统计在[√l, √r]范围内的质数个数,这些质数的平方就是特殊数字
- 返回总数字个数减去特殊数字个数
时间复杂度主要在筛质数阶段,后续统计是O(1)的。
代码实现
class Solution {
public:
int nonSpecialCount(int l, int r) {
int maxSqrt = sqrt(r) + 1;
vector<bool> isPrime(maxSqrt + 1, true);
isPrime[0] = isPrime[1] = false;
// 埃拉托斯特尼筛法
for (int i = 2; i * i <= maxSqrt; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= maxSqrt; j += i) {
isPrime[j] = false;
}
}
}
int specialCount = 0;
int minSqrt = ceil(sqrt(l));
for (int i = minSqrt; i <= maxSqrt; i++) {
if (isPrime[i] && (long long)i * i >= l && (long long)i * i <= r) {
specialCount++;
}
}
return r - l + 1 - specialCount;
}
};
class Solution:
def nonSpecialCount(self, l: int, r: int) -> int:
import math
max_sqrt = int(math.sqrt(r)) + 1
is_prime = [True] * (max_sqrt + 1)
is_prime[0] = is_prime[1] = False
# 埃拉托斯特尼筛法
for i in range(2, int(math.sqrt(max_sqrt)) + 1):
if is_prime[i]:
for j in range(i * i, max_sqrt + 1, i):
is_prime[j] = False
special_count = 0
min_sqrt = math.ceil(math.sqrt(l))
for i in range(min_sqrt, max_sqrt + 1):
if is_prime[i] and l <= i * i <= r:
special_count += 1
return r - l + 1 - special_count
public class Solution {
public int NonSpecialCount(int l, int r) {
int maxSqrt = (int)Math.Sqrt(r) + 1;
bool[] isPrime = new bool[maxSqrt + 1];
Array.Fill(isPrime, true);
isPrime[0] = isPrime[1] = false;
// 埃拉托斯特尼筛法
for (int i = 2; i * i <= maxSqrt; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= maxSqrt; j += i) {
isPrime[j] = false;
}
}
}
int specialCount = 0;
int minSqrt = (int)Math.Ceiling(Math.Sqrt(l));
for (int i = minSqrt; i <= maxSqrt; i++) {
if (isPrime[i] && (long)i * i >= l && (long)i * i <= r) {
specialCount++;
}
}
return r - l + 1 - specialCount;
}
}
var nonSpecialCount = function(l, r) {
const maxSqrt = Math.floor(Math.sqrt(r)) + 1;
const isPrime = new Array(maxSqrt + 1).fill(true);
isPrime[0] = isPrime[1] = false;
// 埃拉托斯特尼筛法
for (let i = 2; i * i <= maxSqrt; i++) {
if (isPrime[i]) {
for (let j = i * i; j <= maxSqrt; j += i) {
isPrime[j] = false;
}
}
}
let specialCount = 0;
const minSqrt = Math.ceil(Math.sqrt(l));
for (let i = minSqrt; i <= maxSqrt; i++) {
if (isPrime[i] && i * i >= l && i * i <= r) {
specialCount++;
}
}
return r - l + 1 - specialCount;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(√r log log √r) | 埃拉托斯特尼筛法的复杂度 |
| 空间复杂度 | O(√r) | 存储质数筛的布尔数组 |
相关题目
- . Count Primes (Medium)