751. IP to CIDR

Given a start IP address ip and a number of ips we need to cover n, return a representation of the range as a list (of smallest possible length) of CIDR blocks.

A CIDR block is a string consisting of an IP, followed by a slash, and then the prefix length. For example: "123.45.67.89/20". That prefix length "20" represents the number of common prefix bits in the specified range.

Example 1:

Input: ip = "255.0.0.7", n = 10
Output: ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"]
Explanation:
The initial ip address, when converted to binary, looks like this (spaces added for clarity):
255.0.0.7 -> 11111111 00000000 00000000 00000111
The address "255.0.0.7/32" specifies all addresses with a common prefix of 32 bits to the given address,
ie. just this one address.

The address "255.0.0.8/29" specifies all addresses with a common prefix of 29 bits to the given address:
255.0.0.8 -> 11111111 00000000 00000000 00001000
Addresses with common prefix of 29 bits are:
11111111 00000000 00000000 00001000
11111111 00000000 00000000 00001001
11111111 00000000 00000000 00001010
11111111 00000000 00000000 00001011
11111111 00000000 00000000 00001100
11111111 00000000 00000000 00001101
11111111 00000000 00000000 00001110
11111111 00000000 00000000 00001111

The address "255.0.0.16/32" specifies all addresses with a common prefix of 32 bits to the given address,
ie. just 11111111 00000000 00000000 00010000.

In total, the answer specifies the range of 10 ips starting with the address 255.0.0.7 .

There were other representations, such as:
["255.0.0.7/32","255.0.0.8/30", "255.0.0.12/30", "255.0.0.16/32"],
but our answer was the shortest possible.

Also note that a representation beginning with say, "255.0.0.7/30" would be incorrect,
because it includes addresses like 255.0.0.4 = 11111111 00000000 00000000 00000100 
that are outside the specified range.

Note:

  1. ip will be a valid IPv4 address.
  2. Every implied address ip + x (for x < n) will be a valid IPv4 address.
  3. n will be an integer in the range [1, 1000].
这道题虽然是easy题,但是细节特别多,错了很多次。

主要思路是 把ip地址转换成long数值,然后取得其最低1位step和n对比,当大于n时,说明以当前位为基准比它小的位数可以组合成的地址数要大于n个,因为要找最少个,所以要所小step的值,直到step <= n,这个时候可以构建块,然后更新n和ip值,构建下一个块,直到找到所有的组合,代码逻辑如下:

vector<string> ipToCIDR(string ip, int n) {
        long ipnum = ipToLong(ip);
        vector<string> ret;
        while(n > 0) {
            long step = ipnum & (-ipnum); // 最低1位 0000 0110->0000 0010
            while (step > n) step /= 2; //一位代表2个地址所以是/2
            ret.push_back(longToBlock(ipnum, step));
            ipnum += step;//当step <n 时更新ipnum为新的没有包含的地址值
            n -= step;//更新n为剩下没有包含的地址值
        }
        return ret;
    }

最低1位的意义:

eg. 255.0.0.8 -> 11111111 00000000 00000000 00001000,其最低1位即为 00000000 00000000 00000000 00001000,使用 x & -x可得x的最低1位

当最低1位step = 00001000,其含义为,比它低的位数能组成的地址范围为:

00001000

00001001 

00001010

……

00001111

step = 8,比它小的位数组成的地址范围000-111一共8个

细节问题:

1 记得更新ipnum和n

2 将ip string转换成long num时,eg 255.0.0.7不是直接把 255 + 7,是255 *256 ^3 + 0 * 256^2 + 0 * 256 + 7,所以一定是num*256 + ip的每一段的整数值

3 因为我是判断每次遇到‘.'的时候进行计算,所以要注意最后一段一定要单独加上去

4 long 转 string时一定注意,因为地址是32位,4个整数值代替,所以即使是 ip 为0了也要补齐四个,所以不能用while(ip > 0)进行循环。

5 注意 long 转 string时得出的顺序是反的,所以要倒过来构建string,注意格式 三个'.' 一个'/'

代码如下:

class Solution {
public:
    long ipToLong(string ip) {
        int j = 0;
        long num = 0;
        for (int i = 0; i < ip.size(); i++) {
            if (ip[i] == '.') {
               string sub = ip.substr(j, i - j); 
                num = stoi(sub) + num * 256; // *256 不是直接加起来 num += stoi(sub) 
                j = i + 1;
            }
        }
        num = stoi(ip.substr(j)) + num * 256;//不要忘记最后一个8位,因为上面是只有遇到‘.'才处理,而最后一个没有'.'
        return num;
    }
    string longToString(long ip, long step) {
        string res;
        vector<int> ips;
        for (int i = 0; i < 4; i++) {//can't use while (x) 一定是4个
            ips.push_back((int) (ip & 255));
            ip >>= 8;
        }
        // while(x > 0) {
        //     int sub = (int) (x & 255);
        //     x = x>>8;
        //     res = res + to_string(sub) + ".";
        //     cout << x << endl;
        //     cout << res << endl;
        // }
        int len = 33;
        while (step > 0) {
            len--;
            step /= 2;
        }
        for (int i = ips.size() - 1; i >= 0; i--) {
            res = res + to_string(ips[i]) + ".";
        }
        return res.substr(0, res.size() - 1) + "/" + to_string(len);
    }
    vector<string> ipToCIDR(string ip, int n) {
        long ipnum = ipToLong(ip);
        vector<string> ret;
        while(n > 0) {
            long step = ipnum & (-ipnum); // 最低1位 0000 0110->0000 0010
            while (step > n) step /= 2; //二进制一位可以表示2个地址
            ret.push_back(longToString(ipnum, step));
            ipnum += step;
            n -= step;
        }
        return ret;
    }
};





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值