1. 程式人生 > >Leetcode 600.不包含連續1的非負整數

Leetcode 600.不包含連續1的非負整數

這一 pan -a bubuko 轉移 for har inf 例子

不包含連續1的非負整數

給定一個正整數 n,找出小於或等於 n 的非負整數中,其二進制表示不包含 連續的1 的個數。

示例 1:

輸入: 5

輸出: 5

解釋:

下面是帶有相應二進制表示的非負整數<= 5:

0 : 0

1 : 1

2 : 10

3 : 11

4 : 100

5 : 101

其中,只有整數3違反規則(有兩個連續的1),其他5個滿足規則。

說明: 1 <= n <= 109

思路

考慮一種比較簡單的情況,如果n=2^k - 1,其中k為正整數,那麽問題就變成二進制數00……0(k個0)到11……1(k個1)中有幾個數不包含連續的1,設答案為f(k)。

我們可以考慮k位二進制數的第一位:如果第一位是0,那麽第二位既可以取0也可以取1,也就是說對後面的k-1位無影響,所以第一位為0

的滿足條件的數總共有f(k-1)個;如果第一位是1,那麽由於不能出現連續的1,第二位只能取0,但是對後面的k-2位無影響,所以第一位為1的滿足條件的數總共有f(k-2)個。

這樣,我們就得到了:f(k) = f(k-1) + f(k-2)。邊界條件為f(1)=2以及f(2)=3,由於f(0)=1滿足原問題的題意也滿足上述的轉移方程,故可以取邊界條件f(0)=1,f(1)=2。

對於n不是2^k-1的一般情況,與上一點的不同之處在於:上一點中只要滿足二進制位長度不超過k,那麽這個數就不會超過n=2^k - 1,而這種情況需要具體考慮不超過n的數。

假設n的二進制有k位,最高位為1,其二進制為1xx……x(x表示0或1),那麽0到n可分為00……0(k個0)到011……1(一個0,k-1個1)和100……0(一個1,k-1個0)到1xx……x(即n)兩個部分。

前一個部分即0到2^(k-1)-1,這部分中滿足條件的答案為f(k-1);第二部分則需進一步討論:如果n的二進制從左往右第二位為1,即n的形式為11x……x,那麽因為題目要求不能有連續的1,所以這一位只能取0,這樣的數一定小於n,所以後k-2位不受大小的限制,答案為f(k-2),並結束計算;如果n的二進制從左往右第二位為0,即n的形式為10x……x,那麽為滿足不超過n的條件,第二位也只能取0,這樣問題就變為從100……0到10x……x之間有多少滿足條件的數,這樣就可以繼續對n的二進制的後k-2位進一步進行類似的討論。

舉個例子,n=10,二進制為1010:

對於最高位的1,我們將0到1010分為0到111和1000到1010兩部分,前一部分的個數為f(3) = 5。

第二部分為1000到1010,最高位確定取1,而n的二進制從左往右第二位為0,為滿足不超過n的條件,滿足條件的數從左往右第二位只能取0。

n的二進制從左往右第三位為1,這樣我們又可以按i中的方法,把1000到1010再次分成1000到1001和1010兩個部分,前一部分的個數為f(1) = 2。

到n的最低位,為0,故最後一位只能取0,按照之前的算法這一步不會增加答案,但由於n=1010b本身還沒有計入,故再加1。

最後得到答案5+2+1=8。

n的二進制長度為log(n),故該算法的時間復雜度為O(log(n))。

第一種情況示意

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

第二種情況示意:

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

 1 class Solution {
 2     public int findIntegers(int num) {
 3         if(num==0) return 1;
 4         String binary = Integer.toBinaryString(num);
 5         int len=binary.length();
 6         int[] f = new int[len+1];
 7         f[0]=1;
 8         f[1]=2;
 9         //計算場i的二進制位符合要求的個數
10         for(int i=2; i<=len; i++) {
11             f[i] = f[i-1]+f[i-2];
12         }
13         //計算0~n的符合要求的總個數
14         int sum=0;
15         for(int i=0, k=len; i<len; i++,k--) {
16             if(binary.charAt(i)==‘1‘) {
17                 sum+=f[k-1];
18                 if(i>0 && binary.charAt(i-1)==‘1‘) {
19                     return sum;
20                 }
21             }
22         }
23         //先前沒有return,到這裏,說明n本身沒有算進去
24         sum++;
25         return sum;
26     }
27 }

Leetcode 600.不包含連續1的非負整數