|
| 1 | +#!/usr/bin/python3 |
| 2 | +""" |
| 3 | +Given a start IP address ip and a number of ips we need to cover n, return a |
| 4 | +representation of the range as a list (of smallest possible length) of CIDR |
| 5 | +blocks. |
| 6 | +
|
| 7 | +A CIDR block is a string consisting of an IP, followed by a slash, and then the |
| 8 | +prefix length. For example: "123.45.67.89/20". That prefix length "20" |
| 9 | +represents the number of common prefix bits in the specified range. |
| 10 | +
|
| 11 | +Example 1: |
| 12 | +Input: ip = "255.0.0.7", n = 10 |
| 13 | +Output: ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] |
| 14 | +Explanation: |
| 15 | +The initial ip address, when converted to binary, looks like this (spaces added |
| 16 | +for clarity): |
| 17 | +255.0.0.7 -> 11111111 00000000 00000000 00000111 |
| 18 | +The address "255.0.0.7/32" specifies all addresses with a common prefix of 32 |
| 19 | +bits to the given address, |
| 20 | +ie. just this one address. |
| 21 | +
|
| 22 | +The address "255.0.0.8/29" specifies all addresses with a common prefix of 29 |
| 23 | +bits to the given address: |
| 24 | +255.0.0.8 -> 11111111 00000000 00000000 00001000 |
| 25 | +Addresses with common prefix of 29 bits are: |
| 26 | +11111111 00000000 00000000 00001000 |
| 27 | +11111111 00000000 00000000 00001001 |
| 28 | +11111111 00000000 00000000 00001010 |
| 29 | +11111111 00000000 00000000 00001011 |
| 30 | +11111111 00000000 00000000 00001100 |
| 31 | +11111111 00000000 00000000 00001101 |
| 32 | +11111111 00000000 00000000 00001110 |
| 33 | +11111111 00000000 00000000 00001111 |
| 34 | +
|
| 35 | +The address "255.0.0.16/32" specifies all addresses with a common prefix of 32 |
| 36 | +bits to the given address, |
| 37 | +ie. just 11111111 00000000 00000000 00010000. |
| 38 | +
|
| 39 | +In total, the answer specifies the range of 10 ips starting with the address |
| 40 | +255.0.0.7 . |
| 41 | +
|
| 42 | +There were other representations, such as: |
| 43 | +["255.0.0.7/32","255.0.0.8/30", "255.0.0.12/30", "255.0.0.16/32"], |
| 44 | +but our answer was the shortest possible. |
| 45 | +
|
| 46 | +Also note that a representation beginning with say, "255.0.0.7/30" would be |
| 47 | +incorrect, |
| 48 | +because it includes addresses like 255.0.0.4 = 11111111 00000000 00000000 |
| 49 | +00000100 |
| 50 | +that are outside the specified range. |
| 51 | +Note: |
| 52 | +ip will be a valid IPv4 address. |
| 53 | +Every implied address ip + x (for x < n) will be a valid IPv4 address. |
| 54 | +n will be an integer in the range [1, 1000]. |
| 55 | +""" |
| 56 | +from typing import List |
| 57 | + |
| 58 | + |
| 59 | +# the weights of ip when converting to binary |
| 60 | +weights = [ |
| 61 | + 24, |
| 62 | + 16, |
| 63 | + 8, |
| 64 | + 0, |
| 65 | +] |
| 66 | + |
| 67 | + |
| 68 | +class Solution: |
| 69 | + def ipToCIDR(self, ip: str, n: int) -> List[str]: |
| 70 | + """ |
| 71 | + bit manipulation |
| 72 | + 111, then 32 to cover only one, depends on LSB |
| 73 | + Greedy |
| 74 | + To cover n, can have representation covers > n |
| 75 | +
|
| 76 | + need helper functions, write the main function first |
| 77 | +
|
| 78 | + Iterate LSB to the next LSB skipping 1's |
| 79 | + num += lsb |
| 80 | + """ |
| 81 | + num_ip = self.to_bin(ip) |
| 82 | + ret = [] |
| 83 | + while n > 0: |
| 84 | + lsb = self.get_lsb(num_ip) |
| 85 | + while (1 << lsb) > n: |
| 86 | + lsb -= 1 |
| 87 | + |
| 88 | + cur_cover = 1 << lsb |
| 89 | + n -= cur_cover |
| 90 | + ret.append( |
| 91 | + self.to_ip(num_ip) + f"/{32-lsb}" |
| 92 | + ) |
| 93 | + num_ip += cur_cover |
| 94 | + |
| 95 | + return ret |
| 96 | + |
| 97 | + def to_bin(self, ip): |
| 98 | + ret = 0 |
| 99 | + for n, w in zip(map(int, ip.split(".")), weights): |
| 100 | + ret += n << w |
| 101 | + |
| 102 | + return ret |
| 103 | + |
| 104 | + def to_ip(self, bin): |
| 105 | + ret = [] |
| 106 | + for w in weights: |
| 107 | + ret.append( |
| 108 | + (bin >> w) & 255 |
| 109 | + ) |
| 110 | + return ".".join(map(str, ret)) |
| 111 | + |
| 112 | + def get_lsb(self, n): |
| 113 | + lsb = 0 |
| 114 | + while (n >> lsb) & 1 == 0: |
| 115 | + lsb += 1 |
| 116 | + # n >>= lsb # error |
| 117 | + return lsb |
| 118 | + |
| 119 | + |
| 120 | +if __name__ == "__main__": |
| 121 | + assert Solution().ipToCIDR("60.166.253.147", 12) == ["60.166.253.147/32","60.166.253.148/30","60.166.253.152/30","60.166.253.156/31","60.166.253.158/32"] |
| 122 | + assert Solution().ipToCIDR("255.0.0.7", 10) == ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] |
0 commit comments