201812-3 CIDR合并 100分 484ms

点击前往试题目录:https://blog.csdn.net/best335/article/details/99550556
在这里插入图片描述
这道题比较复杂,个人认为这道题太长了,并且处理逻辑感觉是往届中最难的,太钻牛角尖了,没有之一。
注意:使用vector 不断地erase操作将导致超时

二进制前缀字符串s1,s2子集关系的表示:
s1.find(s2,0)==0 s1是s2的子集

长度为L且大于0的二进制前缀字符串s1,s2的集合关系表示:
----附加条件1:s1[L-1] == ‘0’

----附加条件2:s2[L-1] == ‘1’
s1.substr(0,L-1) = = s1 U s2

#include<iostream>
#include<vector>
#include<map>
#include<cstring>
#include<algorithm>
#include<ctype.h>
using namespace std;
typedef pair<unsigned int,unsigned int> P;
typedef pair<P,P> PP;
const unsigned int INF=1<<31,F3=256,F2=F3*256,F1=F2*256;
int getNum(const string&s){//将字符串数字转换为整形数字 也可以使用stringstream
	int ans=0;
	for(int i=0,ni=s.size();i<ni;++i)ans=ans*10+s[i]-'0';
	return ans;
}
void getBin(const PP&p,string&s){//将点分10进制的IP地址转换为 2进制字符串
	unsigned int n=p.first.first*F1+p.first.second*F2+p.second.first*F3+p.second.second;
	unsigned int i=INF;
	s.clear();
	while(i>0U){
		s+=char('0'+n/i);
		n%=i,i/=2U;
	}
}
bool find(const string& s1,const string&s2,const int&n1,const int&n2,int i=0){//判断s1是否s2的子集
	if(n1<n2) return false;
	for(int i=0;i<n2;++i) if(s1[i]!=s2[i]) return false;
	return true;
}
int main(){
	std::ios_base::sync_with_stdio(false),cin.tie(NULL);
 	int n,U[100000];
	string s,bin;
	PP p;//点分十进制IP地址
	cin>>n,cin.get(),memset(U,0,sizeof(U));
	vector<pair<pair<string,PP>,int> >G;
	for(int i=0,len;i<n;++i){
		getline(cin,s),s="."+s;
		vector<int> v;
		int j=0,nj=s.size(),l=0,dp=0,f=0;
		while(j<nj&&s[j]=='.'){//处理IP
			l=++j;
			while(j<nj&&s[j]>='0'&&s[j]<='9')++j;
			v.emplace_back(getNum(s.substr(l,j-l)));
			++dp;
		}
		len=dp*8;
		if(j<nj&&s[j]=='/'){//处理前缀长度
			l=++j;
			while(j<nj&&s[j]>='0'&&s[j]<='9')++j;
			len=getNum(s.substr(l,j-l));
		}
		for(int k=v.size();k<4;++k)v.emplace_back(0);//补0
		p=PP(P(v[0],v[1]),P(v[2],v[3])),getBin(p,bin);//将IP地址生成二进制字符串
		G.emplace_back(pair<pair<string,PP>,int>(pair<string,PP>(bin,p),len));//放入容器 准备排序
	}
	sort(G.begin(),G.end());//第一步  排序
	for(int n=G.size(),g1=0,g2=1;g2<n;){
		if(find(G[g2].first.first,G[g1].first.first,G[g2].second,G[g1].second))//第二步 判断相邻IP子集关系
			U[g2++]=1;//置废弃标记
		else
			g1=g2++;
	}
	int g1=0,g2=0;
	while(g1<n&&U[g1]==1)++g1;//必须查找到第一个有效的IP位置
	g2=g1+1;
	while(g2<n&&U[g2]==1)++g2;//查找第二个
	for(int n=G.size();g2<n;){//第三步 判断同级前缀IP集合关系
		const int&len1=G[g1].second,&len2=G[g2].second,len=len1-1;
		if(len>-1&&len1==len2&&G[g1].first.first.compare(0,len,G[g2].first.first,0,len)==0&&'0'==G[g1].first.first[len]){
			G[g1].second=len,U[g2]=1;
 			g2=g1--;
			while(g1>-1&&U[g1]==1)--g1;//不断向前查找直到 IP有效 或越界
			if(g1==-1){//向前查找不到 向后查找
				g1=g2++;
				while(g2<n&&U[g2]==1) ++g2;//不断向后查找直到 IP有效 或越界
			}
		}
		else{
			g1=g2++;
			while(g2<n&&U[g2]==1) ++g2;
  		}
	}
	for(int i=0,ni=G.size();i<ni;++i){
		if(U[i]==1) continue;//该IP前缀废弃
		const PP &p=G[i].first.second;
		cout<<p.first.first<<"."<<p.first.second<<"."<<p.second.first<<"."<<p.second.second<<"/"<<G[i].second<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值