Loading

201812-3 CIDR合并

时间限制:1.0s
内存限制:512MB
题目描述:
CIDR合并

分析

根据提示,我们需要

  1. 将ip前缀按照标准型表示后,对其按照ip大小、len大小排序
  2. 进行从小到大合并
  3. 进行同级合并

解法

  1. 选择合适的数据类型表示ip前缀,为了便于排序和合并,使用string数据类型表示ip的二进制串格式。此外使用list用来排序合并,因为有很多插入删除操作,list相较于vector更快(vector最后一个点超时)

    struct IP
    {
        string ip;
        int len;
        bool operator <(const IP& a)const
        {
            return ip == a.ip ? (len < a.len) : (ip < a.ip);
        }
    };
    list<IP> l;
    
  2. 将三种格式的前缀读入并转换成标准型

    三种格式如下:

    • 12.13.14.15/12
    • 101/8
    • 101.6

    可以得到如下规律:

    • 若读入的字符串没有'/',则说明是第三种,其len为('.'的个数+1)*8,否则:
    • 若读入的字符串没有到三个'.',则说明是第二种,否则:
    • 是标准型的第一种

    可以通过逐个字符读入的方法,来进行判断存取。每遇到一个'.'或者'/',则将前面的数字值转换成二进制串存入;当没有满3个'.',则用0补全至三个'.',代码如下:

    //将十进制数值转换成二进制串
    string itos(int val)
    {
        string tmp = "00000000";//既避免补0也避免完成后reverse
        int i;
        for (i = 7; i >= 0 && val; i--)
        {
            tmp[i] = val % 2;
            val /= 2;
        }
        return tmp;
    }
    
    void read()
    {
        char c;
        string tip;
        int val = 0, k = 0, flag = 0;
        while ((c = getchar()) != '\n')
        {
            if (c == '.')
            {
                tip += itos(val);
                val = 0;
                k++;//记录'.'的个数
            }
            else if (c == '/')
            {
                tip += itos(val);
                val = 0;
                flag = 1;//记录是否有'/'
            }
            else
            {
                val *= 10;
                val += c - '0';
            }
        }
        int len = flag ? val : (k + 1) * 8;
        if (!flag)
            tip += itos(val);//注意如果没有'/'则要将最后的val转换成二进制串存入
        int i;
        for (i = k; i < 3; i++)
        {
            tip += "00000000";
        }
        aip.ip = tip;
        aip.len = len;
        l.push_back(aip);
    }
    
  3. 排序

    //错误,list不能用algorithm里的sort
    bool cmp(IP a, IP b)
    {
        if (a.ip == b.ip)
            return a.len < b.len;
        return a.ip < b.ip;
    }
    sort(l.begin(), l.end(), cmp);
    //正确
    l.sort();
    
  4. 从小到大合并

    规则:从begin开始顺序选择a,b元素,若两者可以合并,则一定是b合并成a,去除b,再比较a和b下一个元素c;若两者不能合并,则比较b和c。

    解释:从小到大合并一定是长len2向短len1合并,并且两者前len1位数字相同。根据排序规则我们知道:

    • 如果a.ip == b.ip,则a.len <= b.len,那么a的ip集合一定包含b,可以去除b;
    • 如果a.ip < b.ip,两者len未知,但是若a.len >= b.len则一定不能合并(b.ip > a.ip,b前b.len位一定有一个1对应a此处是0)
    • 如果a.ip < b.ip 且 a.len < b.len,则只有两者前a.len位相同才能合并。

    综上,只要a.ip和b.ip前a.len位相同,则可以合并到a,代码如下:

    void merge1()
    {
        int i;
        for (it1 = l.begin(); it1 != l.end();)//注意在合并的情况下是不需要it1++的
        {
            it2 = it1;
            it2++;//通过指向再自增的方式让it2指向下一个元素
            if (it2 == l.end())//注意it2不要出界
                break;
            for (i = 0; i < it1->len; i++)
            {
                if (it1->ip[i] != it2->ip[i])
                    break;
            }
            if (i == it1->len)
                l.erase(it2);
            else
                it1++;
        }
    }
    
  5. 同级合并

    规则:若a.len == b.len,且a' = a.ip / len-1为a和b的并集,则去除a,b,加入a',此时若a'前面还有元素,则从该元素开始合并。

    解释:对于两个len相同的同级ip,想要合并一定是第len位一个为0一个为1,这样他们合并之后的ip就是第len位为0的那个的ip(即ip小的,a),len为之前的len-1。例如:len-1和len位分别为00,01,10,11的四种情况,12可以合并为00/len-1,34可以合并为10/len-1。代码如下:

    void merge2()
    {
        int i, len;
        for (it1 = l.begin(); it1 != l.end(); )
        {
            it2 = it1;
            it2++;
            if (it2 == l.end())
                break;
            len = it1->len;
            if (len == it2->len)
            {
                for (i = 0; i < len - 1; i++)
                {
                    if (it1->ip[i] != it2->ip[i])
                        break;
                }
                if (i == len - 1)
                {
                    l.erase(it2);
                    (it1->len)--;
                    if (it1 != l.begin())
                        it1--;
                }
                else
                    it1++;
            }
            else
                it1++;
        }
    }
    
  6. 输出

    每8个二进制数转换成十进制后输出,记得加'.'和'/'。

    for (it1 = l.begin(); it1 != l.end(); it1++)
    {
        for (int i = 0; i < 32; i += 8)
        {
            val = 0;
            for (int j = i; j < i + 8; j++)
            {
                val = val * 2 + it1->ip[j] - '0';
            }
            printf("%d", val);
            if (i != 24)
                putchar('.');
        }
        printf("/%d\n", it1->len);
    }
    

完整代码

posted @ 2021-08-03 18:15  Imane219  阅读(173)  评论(0)    收藏  举报
hi