Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

751. IP to CIDR - Leetcode Solution

Code Implementation

from typing import List

def ipToCIDR(ip: str, n: int) -> List[str]:
    def ip2int(ip):
        parts = list(map(int, ip.split('.')))
        res = 0
        for part in parts:
            res = res * 256 + part
        return res

    def int2ip(x):
        return '.'.join(str((x >> (8 * i)) & 255) for i in reversed(range(4)))

    res = []
    start = ip2int(ip)
    while n > 0:
        max_size = start & -start
        max_block = 1
        while max_block & start == 0:
            max_block <<= 1
        while max_block > n:
            max_block >>= 1
        size = max(max_size, max_block)
        while size > n:
            size >>= 1
        mask = 32 - size.bit_length() + 1
        res.append(f"{int2ip(start)}/{mask}")
        start += size
        n -= size
    return res
      
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<string> ipToCIDR(string ip, int n) {
        vector<string> res;
        long start = ip2long(ip);
        while (n > 0) {
            long max_size = start & -start;
            long size = 1;
            while (size & start == 0) size <<= 1;
            while (size > n) size >>= 1;
            size = max(max_size, size);
            while (size > n) size >>= 1;
            int mask = 32 - bit_length(size) + 1;
            res.push_back(long2ip(start) + "/" + to_string(mask));
            start += size;
            n -= size;
        }
        return res;
    }
private:
    long ip2long(string ip) {
        long res = 0;
        int val = 0;
        for (char c : ip) {
            if (c == '.') {
                res = res * 256 + val;
                val = 0;
            } else {
                val = val * 10 + (c - '0');
            }
        }
        res = res * 256 + val;
        return res;
    }
    string long2ip(long x) {
        return to_string((x >> 24) & 255) + "." + to_string((x >> 16) & 255) + "." +
               to_string((x >> 8) & 255) + "." + to_string(x & 255);
    }
    int bit_length(long x) {
        int len = 0;
        while (x) {
            ++len;
            x >>= 1;
        }
        return len;
    }
};
      
import java.util.*;

class Solution {
    public List<String> ipToCIDR(String ip, int n) {
        List<String> res = new ArrayList<>();
        long start = ip2long(ip);
        while (n > 0) {
            long max_size = start & -start;
            long size = 1;
            while ((size & start) == 0) size <<= 1;
            while (size > n) size >>= 1;
            size = Math.max(max_size, size);
            while (size > n) size >>= 1;
            int mask = 32 - Long.toBinaryString(size).length() + 1;
            res.add(long2ip(start) + "/" + mask);
            start += size;
            n -= size;
        }
        return res;
    }
    private long ip2long(String ip) {
        String[] parts = ip.split("\\.");
        long res = 0;
        for (String part : parts) {
            res = res * 256 + Integer.parseInt(part);
        }
        return res;
    }
    private String long2ip(long x) {
        return String.format("%d.%d.%d.%d", (x >> 24) & 255, (x >> 16) & 255, (x >> 8) & 255, x & 255);
    }
}
      
function ipToCIDR(ip, n) {
    function ip2int(ip) {
        return ip.split('.').reduce((acc, x) => acc * 256 + Number(x), 0);
    }
    function int2ip(x) {
        return [(x >>> 24) & 255, (x >>> 16) & 255, (x >>> 8) & 255, x & 255].join('.');
    }
    let res = [];
    let start = ip2int(ip);
    while (n > 0) {
        let max_size = start & -start;
        let size = 1;
        while ((size & start) === 0) size <<= 1;
        while (size > n) size >>= 1;
        size = Math.max(max_size, size);
        while (size > n) size >>= 1;
        let mask = 32 - Math.floor(Math.log2(size)) + 1;
        res.push(int2ip(start) + "/" + mask);
        start += size;
        n -= size;
    }
    return res;
}
      

Problem Description

Given a starting IP address as a string ip and an integer n representing the number of IP addresses to cover, your task is to return the smallest list of CIDR (Classless Inter-Domain Routing) blocks that exactly cover the range of n IP addresses starting from ip. Each CIDR block should be in the format a.b.c.d/x, where x is the prefix length. You must not reuse or overlap IPs, and your output must cover all the IPs in the range exactly once. There is at least one valid solution.

Thought Process

The problem is about efficiently grouping a sequence of IP addresses into as few CIDR blocks as possible. At first glance, you might think to simply assign each IP one-by-one, but that would be inefficient and not make use of the compactness that CIDR blocks can provide. The key is to maximize the size of each block (i.e., use the largest possible block at each step) while ensuring that blocks do not overlap and that they align correctly with the starting IP. This requires understanding how IP addresses map to integers and how binary alignment affects block size.

Solution Approach

To solve this problem efficiently, we follow these steps:
  • Convert the IP to an integer: Since IP addresses are just 32-bit numbers, we can convert them to integers for easier arithmetic and bit manipulation.
  • Iterate while addresses remain: At each step, determine the largest block (smallest prefix) that can be used at the current starting IP without exceeding the number of remaining addresses or crossing a binary boundary.
  • Calculate block size: The block size must be a power of two and must not cross a boundary (i.e., the start address must be divisible by the block size). This is determined by the lowest set bit in the start address.
  • Limit block size by remaining n: If the largest possible block at the current address is bigger than n, reduce the block size to fit.
  • Convert back to CIDR: For each chosen block, convert the integer back to dotted IP notation and compute the prefix length.
  • Update state: Move the start pointer forward by the block size and reduce n accordingly.
  • Repeat until done: Continue until all n addresses have been covered.
This approach ensures we use as few blocks as possible by always taking the largest valid block at each step.

Example Walkthrough

Suppose ip = "255.0.0.7" and n = 10.
  • Convert "255.0.0.7" to integer: 4278190087
  • First, the lowest set bit is 1 (block size 1), but since 10 IPs remain, we look for the largest block that fits and aligns.
  • First block: "255.0.0.7/32" (covers 1 IP)
  • Next start: 4278190088 ("255.0.0.8"), 9 IPs left. Now, lowest set bit is 8 (block size 8), but only 9 remain, so we can take a block of 8.
  • Second block: "255.0.0.8/29" (covers 8 IPs: .8 to .15)
  • Next start: 4278190096 ("255.0.0.16"), 1 IP left. Block size is 1.
  • Third block: "255.0.0.16/32"
  • Now, all 10 IPs are covered with 3 blocks: ["255.0.0.7/32", "255.0.0.8/29", "255.0.0.16/32"]
At each step, we chose the biggest valid block that aligns with the start IP and doesn't exceed the remaining count.

Time and Space Complexity

  • Brute-force approach: Assigning each IP individually would take O(n) time and O(n) space, which is inefficient for large n.
  • Optimized approach: The number of CIDR blocks used is at most O(log n) because each block can potentially double in size. Each step takes constant time (bit manipulation and conversion), so the total time complexity is O(log n). Space complexity is also O(log n) due to the result list.
The key optimization is using bitwise operations to quickly find the largest block size at each step.

Summary

The problem challenges us to optimally group a sequence of IP addresses using the fewest CIDR blocks. By converting IPs to integers and leveraging bitwise operations, we can always select the largest block that fits and aligns at each step, resulting in an efficient O(log n) solution. This approach is both elegant and practical, demonstrating the power of understanding binary representations in networking problems.