1. 程式人生 > >【LeetCode】762. Prime Number of Set Bits in Binary Representation

【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;        
	}