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

 

類似題目:

House Robber II

House Robber

Ones and Zeroes

 

參考資料:

https://discuss.leetcode.com/topic/90571/java-solution-dp

https://discuss.leetcode.com/topic/90639/c-non-dp-o-32-fibonacci-solution

https://discuss.leetcode.com/topic/90671/java-o-1-time-o-1-space-dp-solution

http://www.geeksforgeeks.org/count-number-binary-strings-without-consecutive-1s/

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM