【CCF-CSP】201812-3 CIDR合并

 原题链接:计算机软件能力认证考试系统

处理输入的每一条IP前缀

string s; cin>>s;
ip[i].a[0]=ip[i].a[1]=ip[i].a[2]=ip[i].a[3]=ip[i].len=0;
int cnt=0,flag=0;//cnt标记当前处理的是第几段IP地址,flag标记是否有前缀长度
for(int j=0;j<s.size();j++){
    if(s[j]=='.') cnt++;//遇到'.',开始处理下一段IP地址
    else if(s[j]=='/') flag=1;//有前缀长度,开始处理前缀长度
    else{
        if(flag) ip[i].len=ip[i].len*10+s[j]-'0';//记录数据
        else ip[i].a[cnt]=ip[i].a[cnt]*10+s[j]-'0';//记录数据
    }
}
if(!flag) ip[i].len=8*(cnt+1);//如果省略前缀长度,前缀长度为出现的IP地址段数*8

 将所有IP前缀排序,IP地址为第一关键字,前缀长度为第二关键字,从小到大排序

struct IP{
    int a[4],len;
    string s;//IP地址的二进制表示,便于之后合并时比较匹配集的大小关系
    bool operator < (const IP &ip) const{
        for(int i=0;i<4;i++)
            if(a[i]!=ip.a[i]) return a[i]<ip.a[i];//从小到大排序
        return len<ip.len;
    }
}ip[N];

子集合并,从小到大,如果i+1是i的子集,删除i+1

bool iscontain(int x,int y){//IP:x<=y  当IP相同,前缀长度x<=y
    string sx=ip[x].s,sy=ip[y].s;//IP地址的二进制表示,十进制转二进制,以字符串形式存储
    for(int i=0;i<ip[x].len;i++){//如果y是x的子集,那么x的IP前缀与y的IP前缀相同
        if(sx[i]!=sy[i]) return false;
    }
    return true;
}
int p=0;
 
for(int i=1;i<n;i++){
    if(!iscontain(p,i,0)) ip[++p]=ip[i];//不合并赋值,合并时不赋值,达到删除效果
}

同集合并,最后一位受约束的IP地址二进制形式,一个为0,一个为1时可以合并

bool iscontain(int x,int y){
    if(ip[x].len!=ip[y].len) return false;//前缀长度不同,不是同级
    string sx=ip[x].s,sy=ip[y].s;//IP地址的二进制形式
    for(int i=0;i<ip[x].len;i++){
        if(i==ip[x].len-1){//最后一位,两个IP地址二进制形式应该是不同的,一个为0,一个为1
            if(sx[i]==sy[i]) return false;
            else return true;
        }
        if(sx[i]!=sy[i]) return false;//其他位的IP地址二进制形式相同
    }
    return true;
}
for(int i=1;i<n;i++){
    if(iscontain(p,i,1)){//同级
        ip[p].len--;//靠前的前缀长度-1
        while(p>0&&iscontain(p-1,p,1)) ip[--p].len--;//尝试合并p与p-1
    }
    else ip[++p]=ip[i];//不同级,赋值保留
}

代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
#define N 1000010
using namespace std;

struct IP{
    int a[4],len;
    string s;
    bool operator < (const IP &ip) const{
        for(int i=0;i<4;i++)
            if(a[i]!=ip.a[i]) return a[i]<ip.a[i];
        return len<ip.len;
    }
}ip[N];

string fun(int n){//返回IP的二进制形式
    string ret="00000000";
    int idx=8;
    while(n){
        ret[--idx]=n%2+'0';
        n/=2;
    }
    return ret;
}
bool iscontain(int x,int y,int k){//判断是否合并,k为0时子集合并,k为1时同级合并
    if(k&&ip[x].len!=ip[y].len) return false;
    string sx=ip[x].s,sy=ip[y].s;
    for(int i=0;i<ip[x].len;i++){
        if(k&&i==ip[x].len-1){
            if(sx[i]==sy[i]) return false;
            else return true;
        }
        if(sx[i]!=sy[i]) return false;
    }
    return true;
}
int main()
{
    int n; cin>>n;
    for(int i=0;i<n;i++){//处理IP
        string s; cin>>s;
        ip[i].a[0]=ip[i].a[1]=ip[i].a[2]=ip[i].a[3]=ip[i].len=0;
        int cnt=0,flag=0;
        for(int j=0;j<s.size();j++){
            if(s[j]=='.') cnt++;
            else if(s[j]=='/') flag=1;
            else{
                if(flag) ip[i].len=ip[i].len*10+s[j]-'0';
                else ip[i].a[cnt]=ip[i].a[cnt]*10+s[j]-'0';
            }
        }
        if(!flag) ip[i].len=8*(cnt+1);
        for(int j=0;j<4;j++) ip[i].s+=fun(ip[i].a[j]);
    }
    sort(ip,ip+n);//排序
    int p=0;
    for(int i=1;i<n;i++){//子集合并
        if(!iscontain(p,i,0)) ip[++p]=ip[i];
    }
    n=p+1; p=0;//更新列表长度
    for(int i=1;i<n;i++){//同级合并
        if(iscontain(p,i,1)){
            ip[p].len--;
            while(p>0&&iscontain(p-1,p,1)) ip[--p].len--;
        }
        else ip[++p]=ip[i];
    }
    n=p+1;//更新列表长度
    for(int i=0;i<n;i++)
        printf("%d.%d.%d.%d/%d\n",ip[i].a[0],ip[i].a[1],ip[i].a[2],ip[i].a[3],ip[i].len);
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值