【LeetCode】762. Prime Number of Set Bits in Binary Representation
Prime Number of Set Bits in Binary Representation
一堆題放一塊太擠了,還是分開放=.=,也能寫得詳細一點
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;
}