|
| 1 | +# 1680. Concatenation of Consecutive Binary Numbers |
| 2 | +# 🟠 Medium |
| 3 | +# |
| 4 | +# https://leetcode.com/problems/concatenation-of-consecutive-binary-numbers/ |
| 5 | +# |
| 6 | +# Tags: Math - Bit Manipulation - Simulation |
| 7 | + |
| 8 | +import timeit |
| 9 | +from itertools import accumulate |
| 10 | + |
| 11 | +# 1 call |
| 12 | +# » OneLine 0.00264 seconds |
| 13 | +# » ListComprehension 0.00313 seconds |
| 14 | +# » BitCountIntSum 0.00229 seconds |
| 15 | +# » MtrxMultiplication 0.00063 seconds |
| 16 | +# » GeomProgression 0.00025 seconds |
| 17 | +# » GemProg2 0.00013 seconds |
| 18 | + |
| 19 | +# Use list comprehension and cast to string to generate the binary |
| 20 | +# representation of the values 1..n then convert to int and modulo it. |
| 21 | +# |
| 22 | +# Time complexity: O(n) - Where n is the number of binary digits of the |
| 23 | +# result string. |
| 24 | +# Space complexity: O(n) - We need to generate a string of size n binary |
| 25 | +# digits. |
| 26 | +# |
| 27 | +# Runtime: 2681 ms, faster than 51.97% |
| 28 | +# Memory Usage: 23.9 MB, less than 14.17% |
| 29 | +class OneLine: |
| 30 | + def concatenatedBinary(self, n: int) -> int: |
| 31 | + return int("".join([bin(x)[2:] for x in range(1, n + 1)]), 2) % ( |
| 32 | + 10**9 + 7 |
| 33 | + ) |
| 34 | + |
| 35 | + |
| 36 | +# Use list comprehension and cast to string to generate the binary |
| 37 | +# representation of the values 1..n then convert to int and modulo it. |
| 38 | +# |
| 39 | +# Time complexity: O(n) - Where n is the number of binary digits of the |
| 40 | +# result string. |
| 41 | +# Space complexity: O(n) - We need to generate a string of size n binary |
| 42 | +# digits. |
| 43 | +# |
| 44 | +# Runtime: 2449 ms, faster than 55.91% |
| 45 | +# Memory Usage: 15.9 MB, less than 22.05% |
| 46 | +class ListComprehension: |
| 47 | + def concatenatedBinary(self, n: int) -> int: |
| 48 | + # Initialize a string result. |
| 49 | + s = "" |
| 50 | + # Iterate over 1..n |
| 51 | + for num in range(1, n + 1): |
| 52 | + # Add the binary representation of num to s. |
| 53 | + s += bin(num)[2:] |
| 54 | + # Convert s to int and modulo 10^9 + 7 |
| 55 | + return int(s, 2) % (10**9 + 7) |
| 56 | + |
| 57 | + |
| 58 | +# Iterate over the sequence 1..n, keeping track of the current result |
| 59 | +# as an integer. For each value, calculate the number of bits of its |
| 60 | +# binary representation, shift the result bits that many positions to |
| 61 | +# the left and add the current value. |
| 62 | +# |
| 63 | +# Time complexity: O(n) - Where n is the number of values in the input. |
| 64 | +# Space complexity: O(1) - We are only storing two integer values in |
| 65 | +# memory. |
| 66 | +# |
| 67 | +# Runtime: 1524 ms, faster than 82.17% |
| 68 | +# Memory Usage: 13.9 MB, less than 62.02% |
| 69 | +class BitCountIntSum: |
| 70 | + def concatenatedBinary(self, n: int) -> int: |
| 71 | + mod = 10**9 + 7 |
| 72 | + # Initialize an int result. |
| 73 | + res = 0 |
| 74 | + # Initialize the count of bits of the current number. |
| 75 | + bit_count = 0 |
| 76 | + # Iterate over 1..n |
| 77 | + for num in range(1, n + 1): |
| 78 | + # Every time we find a power of 2, increase the bit count. |
| 79 | + if (num & (num - 1)) == 0: |
| 80 | + bit_count += 1 |
| 81 | + # Shift the current bits of res left to accommodate the |
| 82 | + # number of bits of the coming value. |
| 83 | + res = ((res << bit_count) + num) % mod |
| 84 | + # Convert s to int and modulo 10^9 + 7 |
| 85 | + return res |
| 86 | + |
| 87 | + |
| 88 | +# TODO study the O(log(n)) solutions below. |
| 89 | + |
| 90 | +# The next three solutions are not my own code, I added them here for |
| 91 | +# completeness and to study them at a later point. |
| 92 | + |
| 93 | +# https://leetcode.com/problems/concatenation-of-consecutive-binary-numbers/discuss/963549/ |
| 94 | +class MtrxMultiplication: |
| 95 | + def concatenatedBinary(self, n: int) -> int: |
| 96 | + def multiply(X, Y): |
| 97 | + return [ |
| 98 | + [ |
| 99 | + sum(a * b for a, b in zip(X_row, Y_col)) % 1000000007 |
| 100 | + for Y_col in zip(*Y) |
| 101 | + ] |
| 102 | + for X_row in X |
| 103 | + ] |
| 104 | + |
| 105 | + ans, acc, level = [[1], [2], [1]], 1, 1 |
| 106 | + while acc < n: |
| 107 | + M = 2 ** (level + 1) |
| 108 | + |
| 109 | + # number of matrix production in this level |
| 110 | + x = take = min(n, M - 1) - acc |
| 111 | + mat = [[M, 1, 0], [0, 1, 1], [0, 0, 1]] |
| 112 | + |
| 113 | + # for example |
| 114 | + # num^13 = num * num^4 * num^8 |
| 115 | + # num^6 = num^2 * num^4 |
| 116 | + while x > 0: |
| 117 | + if x & 1: |
| 118 | + ans = multiply(mat, ans) |
| 119 | + mat, x = multiply(mat, mat), x >> 1 |
| 120 | + |
| 121 | + acc, level = acc + take, level + 1 |
| 122 | + |
| 123 | + return ans[0][0] |
| 124 | + |
| 125 | + |
| 126 | +# https://leetcode.com/problems/concatenation-of-consecutive-binary-numbers/discuss/963886/ |
| 127 | +class GeomProgression: |
| 128 | + def concatenatedBinary(self, n: int) -> int: |
| 129 | + MOD = 10**9 + 7 |
| 130 | + |
| 131 | + def modInverse(n): |
| 132 | + return pow(n, MOD - 2, MOD) |
| 133 | + |
| 134 | + def sumGeometricSeries(r, n): |
| 135 | + return (pow(r, n, MOD) - 1) * modInverse(r - 1) |
| 136 | + |
| 137 | + def sumBinaryOfLength(n, r): |
| 138 | + res = pow(2, n - 1, MOD) * sumGeometricSeries( |
| 139 | + pow(2, n, MOD), r - pow(2, n - 1, MOD) + 1 |
| 140 | + ) |
| 141 | + res %= MOD |
| 142 | + res += ( |
| 143 | + sumGeometricSeries(pow(2, n, MOD), r - pow(2, n - 1, MOD) + 1) |
| 144 | + - 1 |
| 145 | + - (r - pow(2, n - 1, MOD)) |
| 146 | + ) * modInverse(pow(2, n, MOD) - 1) |
| 147 | + return res % MOD |
| 148 | + |
| 149 | + curr_size = 1 |
| 150 | + res = 0 |
| 151 | + for b in range(n.bit_length(), 0, -1): |
| 152 | + res += sumBinaryOfLength(b, min(n, pow(2, b) - 1)) * curr_size |
| 153 | + res %= MOD |
| 154 | + curr_size *= pow( |
| 155 | + 2, (min(n, pow(2, b) - 1) - pow(2, b - 1) + 1) * b, MOD |
| 156 | + ) |
| 157 | + curr_size %= MOD |
| 158 | + return (res + MOD) % MOD |
| 159 | + |
| 160 | + |
| 161 | +# https://leetcode.com/problems/concatenation-of-consecutive-binary-numbers/discuss/1037323/ |
| 162 | +# |
| 163 | +# Time complexity: O(log^2 n) |
| 164 | +# Space complexity; O(1) ? |
| 165 | +# |
| 166 | +# Runtime: 120 ms, faster than 99.22% |
| 167 | +# Memory Usage: 13.8 MB, less than 79.84% |
| 168 | +class GemProg2: |
| 169 | + def concatenatedBinary(self, n: int) -> int: |
| 170 | + def bin_pow(num): |
| 171 | + return [1 << i for i, b in enumerate(bin(num)[:1:-1]) if b == "1"] |
| 172 | + |
| 173 | + ans, MOD, q = 0, 10**9 + 7, len(bin(n)) - 3 |
| 174 | + |
| 175 | + B = bin_pow((1 << q) - 1) + bin_pow(n - (1 << q) + 1)[::-1] |
| 176 | + C = list(range(1, q + 1)) + [q + 1] * (len(B) - q) |
| 177 | + D = list(accumulate(i * j for i, j in zip(B[::-1], C[::-1])))[::-1][ |
| 178 | + 1: |
| 179 | + ] + [0] |
| 180 | + |
| 181 | + for a, b, c, d in zip(accumulate(B), B, C, D): |
| 182 | + t1 = pow(2, b * c, MOD) - 1 |
| 183 | + t2 = pow(pow(2, c, MOD) - 1, MOD - 2, MOD) |
| 184 | + ans += t2 * ((a - b + 1 + t2) * t1 - b) * pow(2, d, MOD) |
| 185 | + |
| 186 | + return ans % MOD |
| 187 | + |
| 188 | + |
| 189 | +def test(): |
| 190 | + executors = [ |
| 191 | + OneLine, |
| 192 | + ListComprehension, |
| 193 | + BitCountIntSum, |
| 194 | + MtrxMultiplication, |
| 195 | + GeomProgression, |
| 196 | + GemProg2, |
| 197 | + ] |
| 198 | + tests = [ |
| 199 | + [1, 1], |
| 200 | + [3, 27], |
| 201 | + [12, 505379714], |
| 202 | + [23032, 659725770], |
| 203 | + ] |
| 204 | + for executor in executors: |
| 205 | + start = timeit.default_timer() |
| 206 | + for _ in range(1): |
| 207 | + for col, t in enumerate(tests): |
| 208 | + sol = executor() |
| 209 | + result = sol.concatenatedBinary(t[0]) |
| 210 | + exp = t[1] |
| 211 | + assert result == exp, ( |
| 212 | + f"\033[93m» {result} <> {exp}\033[91m for" |
| 213 | + + f" test {col} using \033[1m{executor.__name__}" |
| 214 | + ) |
| 215 | + stop = timeit.default_timer() |
| 216 | + used = str(round(stop - start, 5)) |
| 217 | + cols = "{0:20}{1:10}{2:10}" |
| 218 | + res = cols.format(executor.__name__, used, "seconds") |
| 219 | + print(f"\033[92m» {res}\033[0m") |
| 220 | + |
| 221 | + |
| 222 | +test() |
0 commit comments