1. 程式人生 > >[LeetCode] Non-negative Integers without Consecutive Ones 非負整數不包括連續的1

[LeetCode] Non-negative Integers without Consecutive Ones 非負整數不包括連續的1

Given a positive integer n, find the number of non-negative integers less than or equal to n, whose binary representations do NOT contain consecutive ones.

Example 1:

Input: 5
Output: 5
Explanation: 
Here are the non-negative integers <= 5 with their corresponding binary representations:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
Among them, only integer 3 disobeys the rule (two consecutive ones) and the other 5 satisfy the rule. 

Note: 1 <= n <= 109

這道題給了我們一個數字,讓我們求不大於這個數字的所有數字中,其二進位制的表示形式中沒有連續1的個數。根據題目中的例子也不難理解題意。我們首先來考慮二進位制的情況,對於1來說,有0和1兩種,對於11來說,有00,01,10,三種情況,那麼有沒有規律可尋呢,其實是有的,我們可以參見這個帖子,這樣我們就可以通過DP的方法求出長度為k的二進位制數的無連續1的數字個數。由於題目給我們的並不是一個二進位制數的長度,而是一個二進位制數,比如100,如果我們按長度為3的情況計算無連續1點個數個數,就會多計算101這種情況。所以我們的目標是要將大於num的情況去掉。下面從頭來分析程式碼,首先我們要把十進位制數轉為二進位制數,將二進位制數存在一個字串中,並統計字串的長度。然後我們利用

這個帖子中的方法,計算該字串長度的二進位制數所有無連續1的數字個數,然後我們從倒數第二個字元開始往前遍歷這個二進位制數字符串,如果當前字元和後面一個位置的字元均為1,說明我們並沒有多計算任何情況,不明白的可以帶例子來看。如果當前字元和後面一個位置的字元均為0,說明我們有多計算一些情況,就像之前舉的100這個例子,我們就多算了101這種情況。我們怎麼確定多了多少種情況呢,假如給我們的數字是8,二進位制為1000,我們首先按長度為4算出所有情況,共8種。仔細觀察我們十進位制轉為二進位制字串的寫法,發現轉換結果跟真實的二進位制數翻轉了一下,所以我們的t為"0001",那麼我們從倒數第二位開始往前遍歷,到i=1時,發現有兩個連續的0出現,那麼i=1這個位置上能出現1的次數,就到one陣列中去找,那麼我們減去1,減去的就是0101這種情況,再往前遍歷,i=0時,又發現兩個連續0,那麼i=0這個位置上能出1的次數也到one陣列中去找,我們再減去1,減去的是1001這種情況,參見程式碼如下:

解法一:

class Solution {
public:
    int findIntegers(int num) {
        int cnt = 0, n = num;
        string t = "";
        while (n > 0) {
            ++cnt;
            t += (n & 1) ? "1" : "0"; 
            n >>= 1;
        }
        vector<int> zero(cnt), one(cnt);
        zero[0] = 1; one[0] = 1;
        for (int i = 1; i < cnt; ++i) {
            zero[i] = zero[i - 1] + one[i - 1];
            one[i] = zero[i - 1];
        }
        int res = zero[cnt - 1] + one[cnt - 1];
        for (int i = cnt - 2; i >= 0; --i) {
            if (t[i] == '1' && t[i + 1] == '1') break;
            if (t[i] == '0' && t[i + 1] == '0') res -= one[i];
        }
        return res;
    }
};

下面這種解法其實蠻有意思的,其實長度為k的二進位制數字符串沒有連續的1的個數是一個斐波那契數列f(k)。比如當k=5時,二進位制數的範圍是00000-11111,我們可以將其分為兩個部分,00000-01111和10000-10111,因為任何大於11000的數字都是不成立的,因為有開頭已經有了兩個連續1。而我們發現其實00000-01111就是f(4),而10000-10111就是f(3),所以f(5) = f(4) + f(3),這就是一個斐波那契數列啦。那麼我們要做的首先就是建立一個這個陣列,方便之後直接查值。我們從給定數字的最高位開始遍歷,如果某一位是1,後面有k位,就加上f(k),因為如果我們把當前位變成0,那麼後面k位就可以直接從斐波那契數列中取值了。然後標記pre為1,再往下遍歷,如果遇到0位,則pre標記為0。如果當前位是1,pre也是1,那麼直接返回結果。最後迴圈退出後我們要加上數字本身這種情況,參見程式碼如下: 

解法二:

class Solution {
public:
    int findIntegers(int num) {
        int res = 0, k = 31, pre = 0;
        vector<int> f(32, 0);
        f[0] = 1; f[1] = 2;
        for (int i = 2; i < 31; ++i) {
            f[i] = f[i - 2] + f[i - 1];
        }
        while (k >= 0) {
            if (num & (1 << k)) {
                res += f[k];
                if (pre) return res;
                pre = 1;
            } else pre = 0;
            --k;
        }
        return res + 1;
    }
};

類似題目:

參考資料: