上周报名leetcode一周一次的contest,但没注意是限时一个半小时内做完,误了时间。尽管如此,还是尝试去做了constest中的四道题。第一题很简单,第二题就是751题,即将一个IP地址转化为CIDR。
以题目所给例子为例。参数中给出的IP是“255.0.0.7”,要涵盖的地址数目是10。“255.0.0.7”转化成二进制表示法就是“11111111.00000000.00000000.00000111”。所谓涵盖10个地址,就是将包括“255.0.0.7”在内的后10个地址统一用起始地址+前缀的方法表示。“11111111.00000000.00000000.00000111”末位为1,没有0存在,只能用其表示2^0=1个地址,用“255.0.0.7/32”表达,其中32表示前缀长度;下一个地址“255.0.0.8”的二进制表达是“11111111.00000000.00000000.00001000”,末位包括3个0,足够表示2^3,即8个地址,此时前缀长度为32-3=29,故最终的表达式为“255.0.0.8/29”;此时,还剩下10-1-8=1个地址需要覆盖,而在涵盖过上8个地址之后,下一地址“255.0.0.16”的二进制表达是“11111111.00000000.00000000.00010000”,再容纳1个地址绰绰有余,因此此时前缀长度变为32,相应表达式就是“255.0.0.16/32”。最终的结果就是字符串数组[“255.0.0.7/32”,“255.0.0.8/29”,“255.0.0.16/32”]。
具体的C++解法如下所示:
class Solution {
public:
string swt(string ip){
string ret;
int leng=ip.size(),i=0,multiplier=1,res=0;
while(i<leng)
{
char c=ip[leng-i-1];
int s=c-'0'; //转换成数字res
res+=s*multiplier;
multiplier*=10;
i++;
}
int k=0,remain=res;
while((int)(pow(2.0,k*1.0))<=res) k++; //需要用k位加以表示
int p=8-k; //不足8位之处用0填满
while(p>0) { ret+='0'; p--; }
while(k>0)
{
int cmp=(int)(pow(2.0,k*1.0-1));
if(remain>=cmp) { ret+='1'; remain-=cmp; }
else ret+='0';
k--;
}
return ret;
}
string convert(string ip){
string res="";
int p1,p2,p3,leng=ip.length();
string temp,tp;
p1=ip.find('.'); //找第一个下标点的位置
temp=ip.substr(0,p1); //取第一个地址的字符串表达
tp=swt(temp);
res+=tp; res+='.';
p2=ip.find('.',p1+1); //找第二个下标点的位置
temp=ip.substr(p1+1,p2-p1-1); //取第二个地址的字符串表达
tp=swt(temp);
res+=tp; res+='.';
p3=ip.find('.',p2+1); //找第三个下标点的位置
temp=ip.substr(p2+1,p3-p2-1); //取第三个地址的字符串表达
tp=swt(temp);
res+=tp; res+='.';
temp=ip.substr(p3+1,leng-p3-1); //取最后一个地址的字符串表达
tp=swt(temp);
res+=tp;
return res;
}
string change(string rr,int number) //字符串相加
{
int p1,p2,p3; //分别记录三个下标点的位置
int a,b,c,d;
string temp,res;
char t[32];
p1=rr.find('.');
p2=rr.find('.',p1+1);
p3=rr.find('.',p2+1);
temp=rr.substr(0,p1); strcpy(t,temp.c_str());
a=atoi(t);
temp=rr.substr(p1+1,p2-p1-1); strcpy(t,temp.c_str());
b=atoi(t);
temp=rr.substr(p2+1,p3-p2-1); strcpy(t,temp.c_str());
c=atoi(t);
temp=rr.substr(p3+1,rr.length()-p3-1); strcpy(t,temp.c_str());
d=atoi(t);
d+=number; //更新a,b,c,d的值
number=d/256;
d%=256;
if(number!=0)
{
c+=number;
number=c/256;
c%=256;
if(number!=0)
{
b+=number;
number=b/256;
b%=256;
if(number!=0)
{
a+=number;
}
}
}
sprintf(t,"%d.%d.%d.%d",a,b,c,d);
res=t;
return res;
}
vector<string> ipToCIDR(string ip, int range) {
vector<string> res;
string tp;
int num1;
while(1)
{
tp=convert(ip); //转换成二进制表示法
int index=tp.size()-1,u,count=0;
while(tp[index]=='0'||tp[index]=='.') { if(tp[index]=='0') count++; index--; } //当前可用地址数
u=min(count,10); //注意题目中要求最大地址不超过1000
num1=(int)(pow(2.0,u*1.0)); //可容纳的地址数
if(num1>=range) //可容纳地址数在范围内
{
int left=range;
string rr=ip;
while(left>0)
{
int k=0;
string s="",appender;
char t[10];
while((int)(pow(2.0,k*1.0))<=left) k++;
s+=rr;
s+='/';
int number=(int)(pow(2.0,k*1.0-1));
sprintf(t,"%d",33-k);
appender=t;
s+=appender;
res.push_back(s);
left-=number;
rr=change(rr,number);
}
break;
}
else //超过可容纳范围
{
string tr=ip;
tr+='/';
char tt[10];
sprintf(tt,"%d",32-u);
tr+=tt;
res.push_back(tr);
ip=change(ip,num1);
range-=num1;
}
}
return res;
}
};
思路与上面分析的转化的过程是一致的。首先分析当前可容纳的地址数,与需要涵盖的总地址数比较,分两种情形进行处理。题目本身不算难,但复杂在字符串与整型的转换中,同时还要考虑各种边界情况,因此在调试上花了不少时间,不过终于也得到了AC。