751. IP to CIDR
Explanation
Problem Explanation
Given a valid IPv4 address and an integer count 'n', we are supposed to generate 'n' IP addresses in the shortest possible blocks that will cover the desired range. These are represented in CIDR blocks. Classless Inter-Domain Routing (CIDR) is a method for allocating IP addresses and routing IP packets.
CIDR notation is a compact representation of an IP address and its associated routing prefix. For instance, "192.0.2.0/24" represents the IPv4 address 192.0.2.0 and its associated routing prefix 192.0.2.0, or equivalently, its subnet mask 255.255.255.0, which has 24 leading 1-bits. This notation describes the operation of a network segment.
The solution treats the address as a 32-bit integer x and covers the range greedily from left to right. A CIDR block of size 2^k must start at an address that is a multiple of 2^k, so the largest block that can start at x is given by the lowest set bit of x, written x & -x (if x is 0, every size is allowed). The steps are:
- Convert the given IPv4 address to a 32-bit integer
x. - While
n > 0:- Set
size = x & -x, the largest block size aligned atx. - Halve
sizeuntilsize <= n, so the block does not cover addresses past the requested range. - The prefix length is
32 - log2(size). Addip(x)/prefixto the result. - Advance
x += sizeand reducen -= size.
- Set
Each step uses the largest block that is both aligned at x and fits in the remaining count, which produces the fewest blocks.
Let's walk through an example:
For example, if the input is "255.0.0.7" and n=10, the address ends in binary ...00000111. The lowest set bit of 7 is 1, so the first block is "255.0.0.7/32" (1 address), leaving n=9. Now x ends in 8 (...00001000), whose lowest set bit is 8, and 8 <= 9, so the next block is "255.0.0.8/29" (8 addresses), leaving n=1. Then x ends in 16, whose lowest set bit is 16; halving gives 1, so the last block is "255.0.0.16/32". The output is ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"].
Python Solution
Note Python integers have no fixed width, so x & -x gives the lowest set bit directly, and size.bit_length() - 1 gives log2(size) for a power of two.
python class Solution: def ipToCIDR(self, ip: str, n: int) -> List[str]: # Convert the dotted address to a 32-bit integer x = 0 for part in ip.split('.'): x = x * 256 + int(part) ans = [] while n > 0: # Largest block that starts at x: the lowest set bit of x # (x == 0 is aligned to every block size) size = x & -x if x else 1 << 32 # Shrink the block until it does not cover more than n addresses while size > n: size >>= 1 prefix = 32 - (size.bit_length() - 1) ip_str = ".".join(str((x >> shift) & 255) for shift in (24, 16, 8, 0)) ans.append(f"{ip_str}/{prefix}") x += size n -= size return ans
Java Solution
Java
class Solution {
public List<String> ipToCIDR(String ip, int n) {
// Convert the dotted address to a 32-bit value stored in a long
long x = 0;
for (String part : ip.split("\\.")) {
x = x * 256 + Integer.parseInt(part);
}
List<String> result = new ArrayList<>();
while (n > 0) {
// Largest block that starts at x: the lowest set bit of x
long size = x == 0 ? (1L << 32) : (x & -x);
// Shrink the block until it does not cover more than n addresses
while (size > n) {
size >>= 1;
}
int prefix = 32 - Long.numberOfTrailingZeros(size);
result.add(longToIP(x) + "/" + prefix);
x += size;
n -= (int) size;
}
return result;
}
// Convert the 32-bit value back to dotted notation
private String longToIP(long x) {
return ((x >> 24) & 255) + "." + ((x >> 16) & 255) + "." + ((x >> 8) & 255) + "." + (x & 255);
}
}
Javascript Solution
javascript
var ipToCIDR = function(ip, n) {
// Use plain arithmetic: JavaScript bitwise operators work on signed 32-bit values
let x = 0;
for (const part of ip.split('.')) {
x = x * 256 + Number(part);
}
const res = [];
while (n > 0) {
// Largest power of two that divides x (x === 0 is aligned to every size)
let size = 1;
while (size < 2 ** 32 && x % (size * 2) === 0) {
size *= 2;
}
// Shrink the block until it does not cover more than n addresses
while (size > n) {
size /= 2;
}
res.push(intToIp(x) + '/' + (32 - Math.log2(size)));
x += size;
n -= size;
}
return res;
function intToIp(v) {
return [Math.floor(v / 2 ** 24) % 256, Math.floor(v / 2 ** 16) % 256,
Math.floor(v / 2 ** 8) % 256, v % 256].join('.');
}
};
C++ Solution
cpp
class Solution {
public:
vector<string> ipToCIDR(string ip, int n) {
vector<string> result;
long long x = ipToLong(ip);
while (n > 0) {
// Largest block that starts at x: the lowest set bit of x
long long size = x == 0 ? (1LL << 32) : (x & -x);
// Shrink the block until it does not cover more than n addresses
while (size > n) {
size >>= 1;
}
int prefix = 32 - __builtin_ctzll(size);
result.push_back(ipStr(x) + "/" + to_string(prefix));
x += size;
n -= size;
}
return result;
}
long long ipToLong(const string& ip) {
long long result = 0;
size_t start = 0;
for (int i = 0; i < 4; i++) {
size_t pos = ip.find('.', start);
result = result * 256 + stoi(ip.substr(start, pos - start));
start = pos + 1;
}
return result;
}
string ipStr(long long ip) {
return to_string(ip >> 24 & 255) + "." +
to_string(ip >> 16 & 255) + "." +
to_string(ip >> 8 & 255) + "." +
to_string(ip & 255);
}
};
C# Solution
csharp
public class Solution {
public IList<string> IpToCIDR(string ip, int n) {
// Convert the dotted address to a 32-bit value stored in a long
long x = 0;
foreach (var part in ip.Split('.')) {
x = x * 256 + int.Parse(part);
}
var ans = new List<string>();
while (n > 0) {
// Largest block that starts at x: the lowest set bit of x
long size = x == 0 ? (1L << 32) : (x & -x);
// Shrink the block until it does not cover more than n addresses
while (size > n) {
size >>= 1;
}
int prefix = 32;
for (long s = size; s > 1; s >>= 1) {
prefix--;
}
ans.Add(LongToIP(x) + "/" + prefix);
x += size;
n -= (int)size;
}
return ans;
}
private string LongToIP(long x) {
return $"{(x >> 24) & 255}.{(x >> 16) & 255}.{(x >> 8) & 255}.{x & 255}";
}
}
In all of these solutions, the important part is to convert the original IP address into an integer, then advance it block by block until all n addresses are covered. Each block is the largest power of two that is aligned at the current address and does not exceed the remaining count.
The loop produces at most about 2 * log2(n) blocks, and each block takes at most 32 halvings, so the time complexity is O(log n). Apart from the output list, the space used is O(1).
Conclusion
The problem of generating IP addresses in the shortest possible CIDR blocks is actually a binary manipulation problem in its essence. By converting the IP addresses to integers and performing bitwise operations, we are able to solve this problem efficiently in multiple programing languages including Python, JavaScript, Java, C++ and C#.
The key takeaway here is the understanding of CIDR notation and how IP addresses can be efficiently manipulated by converting them to integers and using bitwise operations. Once grasped, this concept can prove helpful in solving other similar problems as well.
It's important to note that different programming languages have different ways of implementing bit manipulation and hence the syntax and exact way of solving these problems might look different across the languages. However, the underlying concept remains the same in all cases thus make sure to focus on that.
It is important to understand these low-level details as they can greatly improve your problem solving and debugging skills in many types of programming tasks. They can also be helpful in interview situations where knowledge about how things work under the hood can set you apart from other candidates.
All in all, bit manipulation is a powerful tool and CIDR blocks are a unique application of it. Any developer who understands these will find themselves better equipped to handle many networking-related programming tasks.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhich of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity describes how the time needed
Want a Structured Path to Master System Design Too? Don’t Miss This!