CCF计算机软件能力认证试题练习:201812-3 CIDR合并

这篇博客探讨了在CCF计算机软件能力认证中关于IP地址表示和CIDR(无类别域间路由)合并的问题。文章重点在于如何有效地表示IP地址以便进行排序和匹配,同时介绍了处理不同类型的IP地址(标准、省略后缀、省略长度)的方法,并通过自定义数据类型来简化操作。最后,博主分享了从实践中学习到的要点,包括排序、合并策略以及同级合并的处理。
摘要由CSDN通过智能技术生成

1、如何表示IP地址

2、排序

3、从小到大合并

4、同级合并

最重要的思考如何表示IP地址使得后面的操作简便。操作包括排序和匹配。

总结学到的:

//学到的

//结构体
1、定义数据结构的简单生成临时变量
IP(string s,int len):s(s),len(len){}

2、在struct中重载小于
bool operator <(const IP &a)const{
    return xx;
}


//字符串(输入切割很重要)
3、专门定义一个函数处理字符串转换成base进制
    res=res*base+s[i]-'0'
4、  对char字符数组进行切割:strtok
    char *sp=strtok(str,".");
    while(sp){
        vec.push_back(sp);
        sp=strtok(NULL,".");
    }
5、  find 和substr函数
    int pos=s.find("/");
if(pos!=string::npos)  就是有
    s=ss.substr(0,pos);从0-pos(包括0不包括pos)
6、 getchar 实时输入,不同操作
    记得输入n之后getchar()
    while((ch=getchar())!='\n'){
        if(ch=='.')//.....
    }

//模拟合并操作
7、循环操作是
for(list<IP>::iterator cur=v.begin();cur!=v.end();cur++) ....
8、找清楚逻辑就行~

 总结学到的2:

strchr strchr(s,'.') 用来查找某字符在字符串中首次出现的位置

strncpy  strncpy(b,a+1,10); // 从a+1 这个位置开始10个字符复制到b中

atoi 把字符串转换成整型数的一个函数

 

解1:参考https://blog.csdn.net/SongBai1997/article/details/86663794

自定义数据类型(存储32位地址和前缀长度)


struct IP{
    IP(string s,int len):s(s),len(len){}
    string s;
    int len;
    bool operator <(const IP &a)const{
        return s==a.s?(len<a.len):(s<a.s);
        }
};

list<IP> v;

处理输入的各种类型IP(标准、省略后缀、省略长度)区别在于省略长度没有指明len,标准有4个.

void f(char str[]){
        //存储4段(但只有标准型有4段,剩下的需要填0)
	vector<string> vec;
        //切割方法
	char *sp=strtok(str,".");//分割,记住 
	while(sp){
		vec.push_back(sp);
		sp=strtok(NULL,".");
	} 
	int num=vec.size(); //有num个部分
        //得到前缀数
	string s=vec.back(),l=""; //最后一段可能有前缀数
	vec.pop_back();
	int pos=s.find("/");
	if(pos!=string::npos){
		string ss=s;
		s=ss.substr(0,pos);
		l=ss.substr(pos+1);
	} 
	int len;
	if(l=="") len=num*8;
	else len=stoi(l,10);
	vec.push_back(s);
        //填充0,此时都满足了标准形态。
	while(num<4){
		vec.push_back("0");
		num++;
	}
	//4个部分全部变成字符串存储的32位二进制
	string res="";
	for(int i=0;i<vec.size();i++){
		string t="00000000";
		int temp=stoi(vec[i],10);
		//换成二进制
		int k=7;
		while(temp){
			t[k--]=temp%2+'0';
			temp/=2;
		} 
		res+=t;
	} 
	v.push_back(IP(res,len));
}
//直接getchar处理,直接记住啊~
//更为简单~
getchar();//读取空白符 
for(int i=1;i<=n;i++)  read();

void read(){
	string ip="";
	char ch;
	int len=0,k=0,val=0;
	bool flag=false;
	while((ch=getchar())!='\n'){
		if(ch=='.'){
			k++;ip+=itos(val);val=0;
		}else if(ch=='/'){
			ip+=itos(val);val=0;flag=true;
		}else val=val*10+ch-'0';
	}
	len=(flag?val:(k+1)*8);
	if(!flag) ip+=itos(val);
	while(k<3){
		ip+=itos(0);k++;
	}
	l.push_back(IP(ip,len));
}

排序 

从小到大合并

//非常明显需要两个指针操作
a 与 b(a++)
若b能移除,下一个是a与c
否则下一个是b与c

然后判断何时a>=b,设a的前缀长度为len,那么如果b的前len个字符和a相同
bool check(string p,string q,int len){
	if(len>p.length()||len>q.length()) return false;
	for(int i=0;i<len;i++)
		if(p[i]!=q[i]) return false;
	return true;
}
//变动a

	for(list<IP>::iterator cur=v.begin();cur!=v.end();){
		list<IP>::iterator next=cur;
		next++;
		if(next==v.end()) break;
		IP p1=*cur;IP p2=*next;
		if(check(p1.s,p2.s,p1.len)) v.erase(next);
		else cur++;
	}

 

同级合并

//先要问合法不...
同级合并的区别就在于check的对象变成了
p1.len==p2.len且p1前缀长度减一后依然合法(只要p1.s[p1.len-1]=0就是合法的)
for(list<IP>::iterator cur=v.begin();cur!=v.end();){
		list<IP>::iterator next=cur;
		next++;
		if(next==v.end()) break;
		IP p1=*cur;IP p2=*next;
		//p1.len==p2.len且p1前缀长度减一后依然合法(只要p1.s[p1.len-1]=0就是合法的)
		if(p1.len==p2.len&&p1.len>0&&p1.s[p1.len-1]=='0'){
			IP tmp=p1;
			tmp.len--;
			//如果能合并 
			if(check(tmp.s,p2.s,tmp.len)){
				v.erase(next);
				*(cur)=tmp;
				if(cur!=v.begin()) cur--;
				 
			}else cur++;
		}else cur++;
	} 

打印


//打印输出 

void print(string s,int len)
{
	string s1=s.substr(0,8);
	string s2=s.substr(8,8);
	string s3=s.substr(16,8);
	string s4=s.substr(24,8);
cout<<stoi(s1,2)<<"."<<stoi(s2,2)<<"."<<stoi(s3,2)<<"."<<stoi(s4,2)<<"/"<<len<<endl;
}

//输出 
for(list<IP>::iterator cur=v.begin();cur!=v.end();cur++)
     print((*cur).s,(*cur).len);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值