一堆题放一块太挤了,还是分开放=.=,也能写得详细一点
Problem
求[L,R]这个闭区间内的数字 二进制表示形式 1的个数 为素数 的数量
Example
L = 6, R = 10
6(0101),1的个数为2,2是素数 +1
7(1001),1的个数为2, 2是素数 +1
8(1000),1个1,1不是素数
9(1001),2个1,2是素数 +1
10(1010),2个1,2是素数 +1
一共4个1 ,输出为4
Solution
思路1
暴力解法,遍历L到R,计算每个数1的个数,判断这个数是否为素数
class Solution {
public int countPrimeSetBits(int L, int R) {
int res = 0;
for (int i = L; i <= R; i++) {
int ct = counter(i);//计算1的个数
if (isPrime(ct)) {//判断是否为素数
res++;
}
}
return res;
}
//计算1的个数
int counter(int num) {
int c = 0;
while (num != 0) {
num >>= 1;
//if (num & 1 == 1)
// c++;
c += num & 1;//可简写为这样
}
return 1;
}
//判断是否为素数
public boolean isPrime(int num) {
int tmp = (int) Math.sqrt(num);//获取平方根
for (int i = 2; i <= tmp; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
}
思路2
可以从计算素数的地方优化,也可以从计算1的个数的地方进行优化
1.计算素数这个,因为题目条件范围数字最大为106 大概220次方,也就是最多19个1,可以把1-19的素数都枚举出来(2,3,5,7,11,13,17,19)
2.计算1的个数,java的Integer.bitCount(int)函数(暂时没看懂这个算法),用O(1)的时间就算出来了=.=!,有空了解一下
class Solution {
public int countPrimeSetBits(int L, int R) {
int res = 0;
for (int i = L; i <= R; i++) {
int ct = counter(i);//计算1的个数
if (isPrime(ct)) {//判断是否为素数
res++;
}
}
return res;
}
//计算1的个数(Integer类自带的一个O(1)的解法,万万想不到=.=!)
int counter(int num){
return Integer.bitCount(num);
}
public boolean isPrime(int num) {
return (num == 2 || num == 3 || num == 5 || num == 7 || num == 11 || num ==13 || num == 17 || num == 19);
}
思路3
下面是自己的解法(用Integer.bitCount()替换了原先while求1个数的算法,LeetCode运行时间从20ms左右,到了11ms)时间复杂度为O(n)
只是判断素数那里和答案不一样
//20以内的素数表
private int[] primers = {0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1};
public int countPrimeSetBits(int L, int R) {
int res = 0;
while (L <= R) {
res += primers[Integer.bitCount(L++)];
}
return res;
}
思路4
用 分治 试了试,不过也是10-13毫秒左右,时间上并没什么太大的突破
不过每次递归实际上只计算了1个数。。相当于把水平的for循环迭代改为垂直的递归,白白多了log(n)的空间占用。
//20以内的素数表
private int[] primers = {0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1};
public int countPrimeSetBits(int L, int R) {
if (L <= R) {
int M = (L + R) / 2;
int m = primers[Integer.bitCount(M)];//求解中间
int a = countPrimeSetBits(L, M - 1);//递归求解左边
int b = countPrimeSetBits(M + 1, R);//递归求解右边
return a + b + m;//把左中右的加起来
}
return 0;
}
接下来就是大佬的show time了
运算最快的人的答案
大佬用位运算把素数存在了一个整数中。。
然后用这个数右移,比如(…101100)右移2,3,5等素数位,最低为一定为1
public int countPrimeSetBits(int L, int R) {
int primes = 0;
primes += 1<<2;
primes += 1<<3;
primes += 1<<5;
primes += 1<<7;
primes += 1<<11;
primes += 1<<13;
primes += 1<<17;
primes += 1<<19;
primes += 1<<23;
primes += 1<<29;
int ans =0;
for(int i=L; i<=R; i++){
if(((primes >> Integer.bitCount(i)) & 1) == 1){
ans++;
}
}
return ans;
}
可以简单优化一下
把那个数字直接算出来(0b1010_0010_1000_1010_1100),16进制0xA28AC
public int countPrimeSetBits(int L, int R) {
int ans =0;
while (L <= R) {
ans += (0xA28AC >> Integer.bitCount(i)) & 1;
}
return ans;
}