原题链接:计算机软件能力认证考试系统
处理输入的每一条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;
}