Leetcode 600.不包含連續1的非負整數
不包含連續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) = 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的非負整數