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);