本文已参与「新人创作礼」活动,一起开启掘金创作之路。
| 每日一题做题记录,参考官方和三叶的题解 |
题目要求
思路一:模拟
统计的数量……DNA动了,那不是昨天树状数组的lowbit么!
首先用数组替代哈希表记录一下质数,然后用lowbit统计当前值二进制中的数量与表作比较即可。
Java
class Solution {
static boolean[] hash = new boolean[35];
static {
int[] prime = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; //质数表
for(int p : prime)
hash[p] = true;
}
public int countPrimeSetBits(int left, int right) {
int res = 0;
for(int i = left; i <= right; i++) {
int x = i, cnt = 0;
while(x != 0 && ++cnt >= 0) //统计计算置位
x -= (x & -x); //lowbit
if(hash[cnt])
res++;
}
return res;
}
}
- 时间复杂度:,
lowbit的复杂度为 - 空间复杂度:
C++
【因为没有静态初始化块,所以就写里面了。】
class Solution {
public:
int countPrimeSetBits(int left, int right) {
bool hash[35];
memset(hash, false, 35);
int prime[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
for(int p : prime)
hash[p] = true;
int res = 0;
for(int i = left; i <= right; i++) {
int x = i, cnt = 0;
while(x != 0 && ++cnt >= 0) //统计计算置位
x -= (x & -x); //lowbit
if(hash[cnt])
res++;
}
return res;
}
};
- 时间复杂度:
- 空间复杂度:
思路二:分治
更优的统计二进制中的数量的方法是分治思路,分组进行统计。
分治统计的个数
- 思路:
- 将待求的两位一组进行划分,统计每组内的个数,将结果写在当前组所在的位置上,得到新的;
- 将更新的四位一组进行划分,计算当前组内的个数,将结果写在当前组所在的位置上,得到新的$val;
- ……八……;
- ……十六……;
- 此时得到的即为当前值含有的的数量。
- 原数值可看作一位一组划分并统计的结果;
- 这个图画的不错 ;
- 其时间复杂度仅为。
Java
class Solution {
static boolean[] hash = new boolean[35];
static {
int[] prime = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; //质数表
for(int p : prime)
hash[p] = true;
}
public int countPrimeSetBits(int left, int right) {
int res = 0;
for(int i = left; i <= right; i++) {
int x = i;
x = (x & 0x55555555) + ((x >>> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >>> 2) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >>> 4) & 0x0f0f0f0f);
x = (x & 0x00ff00ff) + ((x >>> 8) & 0x00ff00ff);
x = (x & 0x0000ffff) + ((x >>> 16) & 0x0000ffff);
if(hash[x])
res++;
}
return res;
}
}
- 时间复杂度:
- 空间复杂度:
C++
【注意无符号右移】
class Solution {
public:
int countPrimeSetBits(int left, int right) {
bool hash[35];
memset(hash, false, 35);
int prime[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
for(int p : prime)
hash[p] = true;
int res = 0;
for(int i = left; i <= right; i++) {
unsigned int x = i;
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);
if(hash[x])
res++;
}
return res;
}
};
- 时间复杂度:
- 空间复杂度:
思路三:库函数
枚举+挨个判断
Java
class Solution {
private boolean isPrime(int x) {
if(x < 2)
return false;
for(int i = 2; i * i <= x; ++i)
if(x % i == 0)
return false;
return true;
}
public int countPrimeSetBits(int left, int right) {
int res = 0;
for(int x = left; x <= right; ++x)
if(isPrime(Integer.bitCount(x)))
res++;
return res;
}
}
- 时间复杂度:,判断是否为质数的复杂度为
- 空间复杂度:
C++
库函数builtin_popcount用于统计二进制数中的个数。
class Solution {
bool isPrime(int x) {
if(x < 2)
return false;
for(int i = 2; i * i <= x; ++i)
if(x % i == 0)
return false;
return true;
}
public:
int countPrimeSetBits(int left, int right) {
int res = 0;
for(int x = left; x <= right; ++x)
if(isPrime(__builtin_popcount(x)))
res++;
return res;
}
};
- 时间复杂度:
- 空间复杂度:
思路四:常数复杂度判断质数
由数据范围可以判定二进制中的个数不会超过即
可以用一个二进制数来表示,质数位为,也就是把前面的哈希表写成一个数,即
那么只要把目标二进制数中的个数与之按位与,结果不为则为质数。
Java
class Solution {
public int countPrimeSetBits(int left, int right) {
int res = 0;
for(int x = left; x <= right; ++x)
if(((1 << Integer.bitCount(x)) & 665772) != 0)
res++;
return res;
}
}
- 时间复杂度:
- 空间复杂度:
C++
class Solution {
public:
int countPrimeSetBits(int left, int right) {
int res = 0;
for(int x = left; x <= right; ++x)
if(((1 << __builtin_popcount(x)) & 665772) != 0)
res++;
return res;
}
};
- 时间复杂度:
- 空间复杂度:
总结
今天的题做得很顺利,想到了昨天那个lowbit也是很快乐。
分治的方法本来打算直接背算了,后来推了几遍发现了思路。
后两种是看到官方用了个库函数还挺有意思。
一二侧重于优化计算的数量,三四侧重于优化质数的判断,其实是可以结合起来用的。
那么、虽然没有假期、但是……春天快乐!
| 欢迎指正与讨论! |