每日一题做题记录,参考官方和三叶的题解 |
题目要求
思路一:模拟
统计
1
1
1的数量……DNA动了,那不是昨天树状数组的lowbit
么!
首先用数组替代哈希表记录一下质数,然后用lowbit
统计当前值二进制中
1
1
1的数量与表作比较即可。
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;
}
}
- 时间复杂度:
O
(
(
r
i
g
h
t
−
l
e
f
t
)
∗
log
r
i
g
h
t
)
O((right-left)*\log right)
O((right−left)∗logright),
lowbit
的复杂度为 O ( log r i g h t ) O(\log right) O(logright) - 空间复杂度: O ( C ) O(C) O(C)
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;
}
};
- 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log r i g h t ) O((right-left)*\log right) O((right−left)∗logright)
- 空间复杂度: O ( C ) O(C) O(C)
思路二:分治
更优的统计二进制中 1 1 1的数量的方法是分治思路,分组进行统计。
分治统计 1 1 1的个数
- 思路:
- 将待求的 v a l val val两位一组进行划分,统计每组内 1 1 1的个数,将结果写在当前组所在的位置上,得到新的 v a l val val;
- 将更新的 v a l val val四位一组进行划分,计算当前组内 1 1 1的个数,将结果写在当前组所在的位置上,得到新的$val;
- ……八……;
- ……十六……;
- 此时得到的 v a l val val即为当前值含有的 1 1 1的数量。
- 原数值可看作一位一组划分并统计的结果;
- 这个图画的不错 ;
- 其时间复杂度仅为 O ( log log n ) O(\log \log n) O(loglogn)。
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;
}
}
- 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log log r i g h t ) O((right-left)*\log \log right) O((right−left)∗loglogright)
- 空间复杂度: O ( C ) O(C) O(C)
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;
}
};
- 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log log r i g h t ) O((right-left)*\log \log right) O((right−left)∗loglogright)
- 空间复杂度: O ( C ) O(C) O(C)
思路三:库函数
枚举+挨个判断
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;
}
}
- 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log r i g h t ) O((right-left)* \sqrt{ \log right}) O((right−left)∗logright),判断是否为质数的复杂度为 O ( x ) O(\sqrt{x}) O(x)
- 空间复杂度: O ( C ) O(C) O(C)
C++
库函数builtin_popcount
用于统计二进制数中
1
1
1的个数。
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;
}
};
- 时间复杂度: O ( ( r i g h t − l e f t ) ∗ log r i g h t ) O((right-left)*\sqrt{ \log right}) O((right−left)∗logright)
- 空间复杂度: O ( C ) O(C) O(C)
思路四:常数复杂度判断质数
由数据范围可以判定二进制中
1
1
1的个数不会超过
19
19
19即
2
,
3
,
5
,
7
,
11
,
13
,
17
,
19
2, 3, 5, 7, 11, 13, 17, 19
2,3,5,7,11,13,17,19
可以用一个二进制数来表示,质数位为
1
1
1,也就是把前面的哈希表写成一个数,即
m
a
s
k
=
10100010100010101100
2
=
665772
mask={10100010100010101100}_2=665772
mask=101000101000101011002=665772
那么只要把目标二进制数中
1
1
1的个数与之按位与,结果不为
0
0
0则为质数。
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;
}
}
- 时间复杂度: O ( ( r i g h t − l e f t ) ) O((right-left)) O((right−left))
- 空间复杂度: O ( C ) O(C) O(C)
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;
}
};
- 时间复杂度: O ( ( r i g h t − l e f t ) ) O((right-left)) O((right−left))
- 空间复杂度: O ( C ) O(C) O(C)
总结
今天的题做得很顺利,想到了昨天那个lowbit也是很快乐。
分治的方法本来打算直接背算了,后来推了几遍发现了思路。
后两种是看到官方用了个库函数还挺有意思。
一二侧重于优化计算
1
1
1的数量,三四侧重于优化质数的判断,其实是可以结合起来用的。
那么、虽然没有假期、但是……春天快乐!
欢迎指正与讨论! |