点击前往试题目录: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;
}