Leetcode 751. IP to CIDR

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 provided solution uses bit manipulation to solve this problem. The steps involved are:

  1. Convert the given IPv4 to binary format.
  2. Loop until count 'n' > 0 :
    • Initialize the lowbit and count.
    • Get the smallest possible prefix and count that accommodates the required range.
    • Once the counting rule is figured out, push the CIDR address to the result.
    • Update the total count and address which is left for iteration.

NOTE: Here, lowbit is a term for the operation of obtaining the smallest possible prefix of an address, which can be calculated by the operation of (x & -x).

Let's walk through an example:

For example, if the input is "255.0.0.7" and n=10, the output will be ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"]. This is the shortest possible block format in CIDR notation for 10 IP addresses starting from "255.0.0.7".

Python Solution

Note To write a python solution, convert current IP address to 10-based integer, then while 'n' is not '0' left shift for 1 and check if new number is smaller than 'n'. When we have found the biggest possible 2^k that satisfies the requirement, we add it to our result list.

1
2python
3class Solution:
4    def ipToCIDR(self, ip: str, n: int) -> List[str]:
5        def ip_to_int(ip):
6            ans = 0
7            for x in ip.split('.'):
8                ans = 256 * ans + int(x)
9            return ans
10
11        def int_to_ip(x):
12            return ".".join(str((x >> i) % 256) for i in (24, 16, 8, 0))
13
14        def get_max(x):
15            if x == 0: return 32
16            p = 0
17            while x < 2 ** 32:
18                x <<= 1
19                p += 1
20            return p
21
22        ans = []
23        x = ip_to_int(ip)
24        while n:
25            max = get_max(x)
26            while 2 ** (max - 1) > n:
27                max -= 1
28            bx = bin(x)[2:].zfill(32)
29            nx = bx[:32 - max + 1] + '0' * (max - 1)
30            ans.append(int_to_ip(int(nx, 2)) + '/' + str(max))
31            x += 2 ** (max - 1)
32            n -= 2 ** (max - 1)
33        return ans

Java Solution

1
2Java
3class Solution {
4  public List<String> ipToCIDR(String ip, int n) {
5    long x = 0;
6    String[] ips = ip.split("\\.");
7    for (int i = 0; i < ips.length; i++) {
8      x = x * 256 + Integer.parseInt(ips[i]);
9    }
10    
11    List<String> result = new ArrayList<>();
12    
13    while (n > 0) {
14      int max = Math.max(33 - Integer.numberOfLeadingZeros(n), 33 - Long.numberOfTrailingZeros(x));
15      long count = Math.min(1L << (32 - max + 1), n);
16      
17      result.add(longToIP(x, max));
18      
19      x += count;
20      n -= count;
21    }
22    
23    return result;
24  }
25
26  //Convert the long format ip address to the standard format
27  private String longToIP(long x, int m) {
28    int[] ans = new int[4];
29    for (int i = 0; i < 4; i++) {
30      ans[3 - i] = (int) x & 255;
31      x = x >> 8;
32    }
33    return ans[0] + "." + ans[1] + "." + ans[2] + "." + ans[3] + "/" + m;
34  }
35}

Javascript Solution

1
2javascript
3var ipToCIDR = function(ip, n) {
4    let ips = ip.split('.').map(Number);
5    let x = ips[0] * 256 ^ 3 + ips[1] * 256 ^ 2 + ips[2] * 256 + ips[3];
6    let res = [];
7    while(n > 0){
8        let mask = Math.max(33 - (n).toString(2).length, 33 - (x & -x).toString(2).length);
9        res.push(intToIp(x, mask));
10        x += 2^(32-mask);
11        n -= 2^(32-mask);
12    }
13    return res;
14    
15    function intToIp(x, mask){
16        return [(x >>> 24) & 255,(x >>> 16) & 255,(x >>> 8) & 255,x & 255].join('.') + `/${mask}`;
17    }
18};

C++ Solution

1
2cpp
3class Solution {
4public:
5    vector<string> ipToCIDR(string ip, int range) {
6        vector<string> result;
7        unsigned int cur = ipToInt(ip);
8        
9        while(range) {
10            int leadingZeros = std::min(__builtin_ctz(cur), __builtin_ctz(range)); // find the leading zeros 
11            result.push_back(ipStr(cur, leadingZeros));
12            range -= (1 << leadingZeros);
13            cur += (1 << leadingZeros);
14        }
15        
16        return result;
17    }
18    
19    unsigned int ipToInt(string ip) {
20        unsigned int result = 0;
21        
22        for(int i = 0, start = 0; i < 4; i++) {
23            int pos = ip.find('.', start);
24            result = (result << 8) + stoi(ip.substr(start, pos-start));
25            start = pos+1;
26        }
27        
28        return result;
29    }
30    
31    string ipStr(unsigned int ip, int lz) {
32        return to_string(ip>>24)        + "." + 
33                    to_string(ip>>16&255) + "." + 
34                    to_string(ip>>8&255)  + "." + 
35                    to_string(ip&255)     + "/" + 
36                    to_string(32-lz);
37    }
38};

C# Solution

1
2csharp
3public class Solution {
4    public IList<string> IpToCIDR(string ip, int range) 
5    {
6        long x = 0;
7        foreach (var part in ip.Split('.'))
8        {
9            x = x * 256 + int.Parse(part);
10        }
11        
12        var ans = new List<string>();
13        while (range > 0)
14        {
15            long maxsize = x & -x,
16            maxval = range > 32
17                ? 1 << (31 - (int)Math.Log(maxsize) / (int)Math.Log(2))
18                : Math.Min(maxsize, range);
19            
20            ans.Add(longToStringIP(x, (int)Math.Log(32)/ (int)Math.Log(2)- (int)Math.Log(maxval)/ (int)Math.Log(2) + 1));
21            x += maxval;
22            range -= (int)maxval;
23        }
24        
25        return ans;
26    }
27
28    string longToStringIP(long x, int step) 
29    {
30        int[] ans = new int[4];
31        ans[0] = (int) (x & 255);
32        x >>= 8;
33        ans[1] = (int) (x & 255);
34        x >>= 8;
35        ans[2] = (int) (x & 255);
36        x >>= 8;
37        ans[3] = (int) x;
38        var ans_str = string.Join(".", ans.Reverse());
39        return ans_str + "/" + step;
40    }
41}

In both of these solutions, the important part is to convert the original IP address into an integer, then increase it by every iteration till we cover all 'n' IP addresses. We compute the smallest possible number of IPs that includes the first IP and ends with 0, then find how much we can give IP addresses with unit '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.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫