其他分享
首页 > 其他分享> > CCF认证 201812-3CIDR合并-带注释

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