CCF认证 201812-3CIDR合并-带注释
作者:互联网
/*
1、排序:IP作为第一关键字,前缀长度作为第二关键字
2、从小到大合并:考虑相邻元素,但是需要注意,要知道他们的表示范围,要能判断包含关系
3、同级合并: 要能求并集。
*/
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
//存储结构
typedef struct {
string ip="";
ll length=0;
ll min;
ll max;
ll sum=0;
} IP;
list<IP> ip;
ll n;
string sub="/", dot=".";
const ll one=1;
//判断a是否是b的子集
bool isSubset(const IP a, const IP b) {
if(a.length<b.length) {
//有效长度越大,集合越小,集合要是小了,那肯定不能是父集了
return false;
}
if(a.min>=b.min&&a.max<=b.max) {
return true;
} else {
return false;
}
}
bool canMerge(const IP&a, const IP&b) {
IP c=a;
c.length-=1;
//offset:低offset要全部为0才算合法ip
ll offset=32-c.length;
//首先判断是否合法,一个是长度,另一个是看是否能保证低offset位为0
if(c.length<1 || c.sum!=((c.sum>>offset)<<offset)) {
return false;
} else {
//a与b的高a.length-1位相同,第a.length位不同,表示并集与a'重合。
if(((a.min>>offset)<<offset)==((b.min>>offset)<<offset) && a.min!=b.min) {
return true;
}
}
return false;
}
int main() {
cin>>n;
while(n--) {
IP temp;
int num_dot=0;
cin>>temp.ip;
//比较灵性的一行代码
for(int i=0; (i=temp.ip.find(dot,i))!=string::npos; num_dot++,i++); //找点的个数
int location;
if((location=temp.ip.find(sub))==string::npos) {
//是省略长度类型,根据点的个数求出长度
temp.length=(num_dot+1)*8;
} else {
//不是省略长度类型,获取长度,并且删除/之后的元素
temp.length=stoll(temp.ip.substr(location+1));
temp.ip.erase(location);
}
if(num_dot<3) {
//是省略后缀类型
for(int i=0; i<3-num_dot; i++) {
temp.ip+=".0";
}
}
//至此成功补全所有输入类型。
//下面计算sum min max,这个循环是用来找.的
int start,end;
for(start=0; (end=temp.ip.find(dot,start))!=string::npos; start=end+1) {
temp.sum<<=8;
temp.sum+=stoll(temp.ip.substr(start,end));
}
//上述运算还差一次,补上
end=temp.ip.find_last_of(dot);
temp.sum<<=8;
//这里的sum就是32位ip地址的十进制表示,保存在了ll型里
temp.sum+=stoll(temp.ip.substr(end+1));
//最小值, 输入合理即可保证低offset位全部为0
temp.min=temp.sum;
int offset=32-temp.length;
//求出高length有效的情况下的最大值
temp.max=temp.min+(one<<offset)-1;
//目前temp内部元素已经全部求出,可以保存了
ip.push_back(temp);
}
//下面开始处理,lambda表达式,很强势的
ip.sort([](const IP&a, const IP&b) {
if(a.sum==b.sum) {
return a.length<b.length;
}
return a.sum<b.sum;
});
//第二步:从小到大合并,节点型容器不支持it+1,所以用一种开挂方法吧:用两个迭代器,两个都参与迭代,但是一个前一个后。
auto it1=ip.begin(),it2=ip.begin();
for(it2++; it2!=ip.end();) {
if(isSubset(*it2,*it1)) {
ip.erase(it2++);
} else {
it1++;
it2++;
}
}
//第二次合并,其实就是将a的长度-1,然后删除b
it1=ip.begin(),it2=ip.begin();
for(it2++; it2!=ip.end();) {
if((*it1).length==(*it2).length&&canMerge(*it1,*it2)) {
//如果可以合并的话,那就a。length-=1 删除b
(*it1).length-=1;
ip.erase(it2++);
if(it1!=ip.begin()) {
it1--;
it2--;
}
} else {
it1++;
it2++;
}
}
for(auto i:ip) {
cout<<i.ip<<"/"<<i.length<<endl;
}
return 0;
}
标签:3CIDR,temp,ip,ll,201812,num,IP,CCF,dot 来源: https://blog.csdn.net/qq_36321889/article/details/88092567