ccf 2018-12-3 CIDR 不使用stl容器

ccf 2018-12-3 CIDR 不使用stl容器

题目:http://118.190.20.162/view.page?gpid=T82

  • 根据题目提供的思路
  • 代码前半段解析字符串那里比较长,但是思路很简单,我使用了编译原理课上讲的状态机,其实是个很简单的东西,下图是我画的简单的图,这种方法我觉得优势在于通用,而且直观,缺点就是看起来代码挺多的(不过其实我觉得那些代码少的,也不过是使用了库函数,比如string的find什么的)
数字
数字
.(点)
/(斜杠)
\0(字符串末尾)
q1
q2
q3
开始
结束
  • 在合并那里,我本来想用list容器,后来想了想,发现一个数组即可,逻辑上可以认为有一个L和L`,但其实为了节省空间,可以只用L即可

  • 至于具体细节,有一些写在注释里了,有一些。。不想写了

运行 109ms 16.37MB

//CIDR
#include <stdio.h>
#include <string>
#include <list>
#include <algorithm>
#include <stdlib.h>

using namespace std;

struct IP{
	public: 
		int p[4];
		int mask;
		int q[4][8];
	IP(){}

	private:
	int num = 0;//用来标记是第几个数字
	//接受字符串状态机
	//q1----数字--->q1
	//q1----"."---->q1
	//q1----"/"---->q2
	//q1----"\0"--->q3

	//读取数字并存入p中
	void q1(char* s,int k)
	{
		p[num] = 0;
		for(; s[k]!='\0'&&isdigit(s[k]); k++){
			p[num] *= 10;
			p[num] += s[k]-'0';
		}
		//状态转移
		if(s[k]=='.'){
			num++;
			q1(s,++k);
		}else if(s[k]=='/'){
			num++;
			q2(s,++k);
		}else if(s[k]=='\0'){
			num++;
			q3();
		}
	}
	//读取掩码,同时如果遇到省略后缀型的,要补0
	void q2(char* s,int k){
		for(int i=num; i<4; i++){//省略后缀型
			p[i] = 0;
		}
	
		mask = 0;
		for(; s[k]!='\0'&&isdigit(s[k]); k++){
			mask *= 10;
			mask += s[k]-'0';
		}
	}
	//省略长度型
	void q3()
	{
		for(int i=num; i<4; i++){
			p[i] = 0;
		}
		mask = num*8;
	}

	void dec2bin(int n,int* s)//十进制转2进制
	{
		int k=7;
		while(n>0){
			s[k--] = n&1;
			n >>= 1;
		}
	}

	public:
	void str2IP(char* s){//解析入口
		num = 0;
		q1(s,0);
		for(int i=0; i<4; i++){
			dec2bin(p[i], (int*)q[i]);	
		}
	}
		
	public:
	bool operator <(const IP& b)const {
		for(int i=0; i<4; i++){
			if(p[i] != b.p[i]){
				return p[i]<b.p[i];
			}
		}
		return mask < b.mask;
	}

	bool operator ==(const IP& b) const {
		return !(*this<b)&&!(b<*this);
	}
};

//判断a的ip地址列表是否包含了b
int isContain(const IP& a,const IP& b)
{
	if(a.mask > b.mask)	return 0;

	int mask = a.mask;//取a的mask
	int flag = 1;
	//判断ip的前mask位是否相同,相同表示包含,否则不包含
	for(int i=0; i<4; i++)
	for(int j=1; j<=8 && i*8+j<=mask; j++)//这里写的风格有点让人不舒服
	{
		if(a.q[i][j-1] != b.q[i][j-1]){
			flag = 0;
			break;
		}
	}
	return flag;
}

//同级合并
int CanMerge(const IP& a,const IP &b)
{
	if(a.mask != b.mask)	return 0;	

	int mask = a.mask;//取a的mask或者b的mask
	int flag = 1;
	//判断ip的前mask-1位是否相同	
	for(int i=0; i<4; i++)
	for(int j=1; j<=8 && i*8+j<=mask-1; j++)
	{
		if(a.q[i][j-1] != b.q[i][j-1]){
			flag = 0;
			break;
		}
	}
	//如果ip前mask-1位相同,且第mask位不同,则可以合并,否则不可以
	return flag  && a.q[(mask-1)/8][(mask-1)%8]!=b.q[(mask-1)/8][(mask-1)%8];
}

IP L[110000];
int main()
{
	//freopen("1.txt","r",stdin);	
	int len;
	int i,k,n;
	char s[50];
	
	scanf("%d", &n);
	len = n;
	for(i=0; i<n; i++){
		scanf("%s", s);
		L[i].str2IP(s);
	}
	
	sort(L, L+len);
	
	//merge1  原地赋值,没有开辟一个新的空间
	i=0;
	k=0;
	while(i<len){
		if(i==0){
			L[k++] = L[i++];
			continue;//L[0]
		}else if(isContain(L[k-1],L[i])){//如果包含,则不需要保存L[i]了
			i++;
			continue;
		}else{//not contain
			L[k++] = L[i++];
			continue;
		}
	}
	len = k;
	
	
	//merge2
	k = 0;
	i = 0;
	while(i<len){//注意这里有i++
		if(i==0){
			L[k++] = L[i++];
		}
		else if(CanMerge(L[i],L[k-1]))
		{
			L[k-1].mask--;
			while(k>2 && CanMerge(L[k-1],L[k-2])){//向前查看是否可以合并 
			//k=2时,列表中只有2个元素,即L[0]和L[1];k=1时,只有一个元素,就不会出现合并了
				L[k-2].mask--;//合并,只留下L[k-2]和L[k-1]的并集
				k--;
			}
			i++;
		}else{
			L[k++]=L[i++];
		}
	}
	len = k;
	
	for(i=0; i<len; i++)
		printf("%d.%d.%d.%d/%d\n",L[i].p[0],L[i].p[1],L[i].p[2],L[i].p[3],L[i].mask);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值