LeetCode 600 - Non-negative Integers without Consecutive Ones


https://www.cnblogs.com/grandyang/p/6959585.html
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
X. DP
https://www.jiuzhang.com/solution/non-negative-integers-without-consecutive-ones/
  • 数字n转化为01字符串,从前向后遍历,f[i]表示当前i为'1'时,i位其右侧01串排列的合法方案。
  • f[i] = f[i - 1] + f[i - 2],此处求出i位的右侧的01串排列的合法方案,可以由i - 1时尾部加上一个'0'转移到i,可以由i - 2时尾部加上'01'转移到i。
  • 从高位向低位遍历,每次遇到为'1'的位时,加上f[i]即可,遇到连续两个'1'时,返回即可
public int findIntegers(int num) { if (num < 2) { return num + 1; } StringBuilder str = new StringBuilder(Integer.toBinaryString(num)).reverse(); int k = str.length(); int[] f = new int[k]; f[0] = 1; f[1] = 2; for (int i = 2; i < k; i++) { //f[i]表示当前i为'1'时,i位其右侧01串排列的合法方案。 f[i] = f[i - 1] + f[i - 2]; //f[i] = f[i - 1] + f[i - 2],此处求出i位的右侧的01串排列的合法方案,可以由i - 1时尾部加上一个'0'转移到i,可以由i - 2时尾部加上'01'转移到i。 } int ans = 0; for (int i = k - 1; i >= 0; i--) { if (str.charAt(i) == '1') { ans += f[i]; //每次遇到为'1'的位时,加上f[i]即可 if (i < k - 1 && str.charAt(i + 1) == '1') { return ans; } } } ans++; //加上自身数字 return ans; }

This problem can be solved using Dynamic Programming. Let a[i] be the number of binary strings of length i which do not contain any two consecutive 1’s and which end in 0. Similarly, let b[i] be the number of such strings which end in 1. We can append either 0 or 1 to a string ending in 0, but we can only append 0 to a string ending in 1. This yields the recurrence relation:
a[i] = a[i - 1] + b[i - 1]
b[i] = a[i - 1] 
The base cases of above recurrence are a[1] = b[1] = 1. The total number of strings of length i is just a[i] + b[i].
Following is the implementation of above solution. In the following implementation, indexes start from 0. So a[i] represents the number of binary strings for input length i+1. Similarly, b[i] represents binary strings for input length i+1.
static  int countStrings(int n)
{
    int a[] = new int [n];
    int b[] = new int [n];
    a[0] = b[0] = 1;
    for (int i = 1; i < n; i++)
    {
        a[i] = a[i-1] + b[i-1];
        b[i] = a[i-1];
    }
    return a[n-1] + b[n-1];
}
  • let one[i] be the number of non-consecutive-1 solutions with the i-th significant bit being 1;
  • let zero[i] be the number of non-consecutive-1 solutions with the i-th significant bit being 0;
the most tricky part is how to count the solutions smaller than num.
We first calculate number of all n-bits solutions: res = one[n - 1] + zero[n - 1].
then we subtract the solutions which is larger than num, we iterate from the MSB to LSB of num:
  • if bit[i] == 1
    • if bit[i + 1] == 0, there's no solutions in res that is larger than num, we go further into smaller bit and subtract.
    • if bit[i + 1] == 1, we know in those res solutions it won't have any consecutive ones. when bit[i + 1] == 1, in one[i + 1], the i-th bit in valid solutions must be 0, which are all smaller than 'num', we don't need to check smaller bits and subtract, so we break form the loop.
  • if bit[i] == 0
    • if bit[i + 1] == 1, there's no solutions in res that is larger than num, we go further into smaller bit and subtract.
    • if bit[i + 1] == 0, we know zero[i + 1] includes solutions of i-th == 0 (00***) and i-th bit == 1 (01***), we know the i-th bit of num is 0, so we need to subtract all the 01*** solutions because it is larger than num. Therefore, one[i] is subtracted from res.
    public int findIntegers(int num) {
        StringBuilder sb = new StringBuilder(Integer.toBinaryString(num)).reverse();
        int n = sb.length();
        
        int a[] = new int[n];
        int b[] = new int[n];
        a[0] = b[0] = 1;
        for (int i = 1; i < n; i++) {
            a[i] = a[i - 1] + b[i - 1];
            b[i] = a[i - 1];
        }
        
        int result = a[n - 1] + b[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            if (sb.charAt(i) == '1' && sb.charAt(i + 1) == '1') break;
            if (sb.charAt(i) == '0' && sb.charAt(i + 1) == '0') result -= b[i];
        }
        
        return result;
    }
X. Memoized DP
public int findIntegers(int num) {
  return findIntegers(num, new HashMap<>());
}    

public int findIntegers(int num, Map<Integer, Integer> memo) {
    if (num <= 2) return num + 1; // base case
    if (memo.containsKey(num)) return memo.get(num); // check if this result has already been computed   
    
    int msb = 31 - Integer.numberOfLeadingZeros(num); // retrieve index of most significant bit
    int subNum = (1 << msb) - 1, subNum2 = ~(1 << msb) & num;
    if (subNum2 >= 1 << msb - 1) subNum2 = subNum >> 1;
    int result = findIntegers(subNum, memo) + findIntegers(subNum2, memo);
    
    memo.put(num, result); // add result to memo
    return result;
}

X. 
https://www.acwing.com/solution/LeetCode/content/467/
  1. 按照数位统计的一般思路,从二进制的高位向底位,每次保证作为答案的数字不能超过 n。
  2. 首先预处理出长度为 i 的二进制串中,以 0 作为最高位且符合条件的二进制串的个数。这里从低位开始往高位递推,递推方程为 f(i)=f(i1)+f(i2),其中,f(0)=f(1)=1f(0) 无具体含义。
  3. 从高到底遍历 n 的二进制位,设计布尔变量 last_one,表示 n 的二进制位中上一位是否为 1,初始为 false。
  4. 若第 i 位为 1,累计答案 f(i+1),然后考察 last_one,若 last_one 为 true,则此时统计工作已经结束,直接返回 ans;否则,设置 last_one = true。
  5. 若第 i 位为 0,设置 last_one = false。
  6. 最终返回 ans + 1,这里的加 1 加的是 n 本身作为合法的答案。
下面以例子 10101100 为例分析:
  1. 第 7 位是 1,则答案累计 f(8),这里累计的是 00000000 - 01111111的答案个数;此时 last_one = false,然后设置 last_one = true。
  2. 第 6 位是 0,设置 last_one = false。
  3. 第 5 位是 1,则答案累计 f(6),这里累计的是 10000000 - 10011111的答案个数;此时 last_one = false,然后设置 last_one = true
  4. 第 4 位是 0,设置 last_one = false。
  5. 第 3 位是 1,则答案累计 f(4),这里累计的是 10100000 - 10100111的答案个数。此时 last_one = false,然后设置 last_one = true。
  6. 第 2 位是 1,则答案累计 f(3),这里累计的是 10101000 - 10101011的答案个数。此时 last_one = true,无需再进行之后的统计,因为第 2 位不能放置 1,所以返回答案为 f(3) + f(4) + f(6) + f(8) = 55。

时间复杂度

  • 只涉及到对二进制位的操作,故时间复杂度为 O(logn)
https://blog.csdn.net/magicbean2/article/details/78968556
确实是一道比较难的题目,参考了网上的解法。该解法基于如下两个观察:
1)在由'0'和'1'构成的长度为k的字符串中,不含连续'1'的字符串的个数为菲波那切数列f(k)。我觉得这个可以严格证明,但是为了简便,这里仅举例说明一下。例如我们假设k = 5,那么字符串的的范围为00000 - 11111。我们可以将该范围分为两个部分:[00000, 01111) U [10000, 10111)。 而任何大于等于110000的数字都不符合条件,因为其必定含有两个连续的1。[00000, 01111)其实就对应了f(4),而[10000, 10111)其实就对应了f(3),所以f(5) = f(4) + f(3)。
2)如果对字符串从左往右扫描,每次遇到一个1的时候,如果它的左边是0并且右面还有k位,那么就可以给结果加上f(k),这是因为我们可以将该位置为0,并且在之后的k位上添加任意合法的长度为k的字符串,其大小都不会超过该字符串并且符合条件。但是如果其左边是1并且后面还有k位,那么添加f(k)之后就应该立即返回,这是因为任何之后添加f(i) (i < k)所对应的结果都不符合条件,原因是当前已经出现了两个连续的1了。如果程序在循环中没有提前返回,那么说明n本身也是符合条件的,所以最后返回ans + 1。
该算法的时间复杂度是O(1),空间复杂度也是O(1),这是因为对于int而言k <= 32,实在是好的不能再好的算法了。当然如果不限制是int,而是任意大小的正整数,那么其时间复杂度和空间复杂度都是O(log(num)),也是非常好的算法了。

public int findIntegers(int num) {
    int[] f = new int[32];
    f[0] = 1;
    f[1] = 2;
    for (int i = 2; i < f.length; i++)
        f[i] = f[i - 1] + f[i - 2];
    int i = 30, sum = 0, prev_bit = 0;
    while (i >= 0) {
        if ((num & (1 << i)) != 0) {
            sum += f[i];
            if (prev_bit == 1) {
                sum--;
                break;
            }
            prev_bit = 1;
        } else
            prev_bit = 0;
        i--;
    }
    return sum + 1;
}
Build a tree to consider all possible number.
Let 1.and 0 for each bit be tree node
compress to 4 possible result for each bit:
  1. all bit before cur is less than num and no continues 1 and cur bit is at one.
  2. all bit before cur is less than num and no continues 1 and cur bit is at zero.
  3. cur and prev bit is equal to num.
  4. larger than num or has contiunes '1'.
then run through the tree.
    public int findIntegers(int num) {
        //one:all bit before cur is less than num and no continues 1 and cur bit is at one;
        //zero:all bit before cur is less than num and no continues 1 and cur bit is at zero;
        int one=0,zero=0,temp;
        boolean isNum=true;
        for(int i=31;i>=0;i--){
            temp=zero;
            zero+=one;
            one=temp;
            if(isNum&&((num>>i)&1)==1){
                zero+=1;
            }
            if(((num>>i)&1)==1&&((num>>i+1)&1)==1){
                isNum=false;
            }
            
        }
        return one+zero+(isNum?1:0);
    }


这道题给了我们一个数字,让我们求不大于这个数字的所有数字中,其二进制的表示形式中没有连续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这种情况
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,那么直接返回结果。最后循环退出后我们要加上数字本身这种情况
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;
}

Numbers WIthout Consecutive 1s in binary representation

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts