题解 | #识别有效的IP地址和掩码并进行分类统计#
识别有效的IP地址和掩码并进行分类统计
https://www.nowcoder.com/practice/de538edd6f7e4bc3a5689723a7435682
#include <iostream>
using namespace std;
#include <string>
#include <sstream>
#include <cmath>
int getNum(string str) {
int num = 0;
if (str.length() == 0) {
return -1;
}
for (int i = str.size() - 1, j = 0; i >= 0; i--, j++) {
if ('0' <= str[i] && str[i] <= '9') {
num = num + (str[i] - '0') * (pow(10, j));
} else {
return -1;
}
}
return num;
}
bool bm(unsigned b) {
if (!b) {
return false;
}
b = ~b + 1;
if (b == 1) {
return false;
}
if ((b & (b - 1)) == 0) {
return true;
}
return false;
}
int main() {
string str;
int A = 0, B = 0, C = 0, D = 0, E = 0, err = 0, p = 0;
// 各种类型的IP地址的计数,也可以使用数组来记,不过这样比较清楚
string ip; // 每轮的ip
string bitMask; // 每轮的子网掩码
string num; // 每轮的数字
int realNum;
int i; // 地址标志
int type; // 类型标志
bool flag; // 合法标志
bool pri; // 私有标志
bool count; // 不计数特殊标志位
int num1; // 私有判断辅助1
int num2; // 私有判断辅助2
unsigned b;
string bmNum;
while (getline(cin, str)) {
istringstream is(str);
getline(is, ip, '~');
is >> bitMask;
// 接下来对ip进行检查
istringstream ipCode(ip);
// 初始化所有标志位
i = 1;
flag = true;
pri = false;
count = true;
b = 0;
type = 0;
while (getline(ipCode, num, '.') && flag) {
realNum = getNum(num);
switch (i) {
case 1: {
num1 = realNum;
if (realNum >= 1 && realNum <= 126) {
type = 1;
} else if (realNum >= 128 && realNum <= 191) {
type = 2;
} else if (realNum >= 192 && realNum <= 223) {
type = 3;
} else if (realNum >= 224 && realNum <= 239) {
type = 4;
} else if (realNum >= 240 && realNum <= 255) {
type = 5;
} else if (realNum == 0 || realNum == 127) {
flag = false;
count = false;
} else {
flag = false;
}
break;
}
case 2: {
num2 = realNum;
if (!(realNum >= 0 && realNum <= 255)) {
flag = false;
}
break;
}
case 3: {
if (!(realNum >= 0 && realNum <= 255)) {
flag = false;
}
break;
}
case 4: {
if (!(realNum >= 0 && realNum <= 255)) {
flag = false;
}
break;
}
}
i++;
}
if (flag && count) {
// 进行私有ip判断
if (num1 == 10) {
pri = true;
} else if (num1 == 172 && (num2 >= 16 && num2 <= 31)) {
pri = true;
} else if (num1 == 192 && num2 == 168) {
pri = true;
}
// 进行子网掩码判断
istringstream bmCode(bitMask);
while (getline(bmCode, bmNum, '.')) {
b = (b << 8) + getNum(bmNum);
}
flag = bm(b);
}
// 最后进行计数
if (count && flag) {
if (pri) {
p++;
}
switch (type) {
case 1:
A++;
break;
case 2:
B++;
break;
case 3:
C++;
break;
case 4:
D++;
break;
case 5:
E++;
break;
}
} else if (count && !flag) {
err++;
}
}
cout << A << " " << B << " " << C << " " << D << " " << E << " " << err << " "
<< p << endl;
}
主要是输入的拆分和掩码的判断
istringstream的功能主要是创建了一个流,可以像cin一样使用getline操作,同时getline又支持自定义分隔符,这样就搞定了输入到数字的转化,接下来就是繁琐的逻辑判断了。
掩码的判断参考了以下思路:
- 如何判断一个掩码地址是不是满足前面连续是1,然后全是0?
- 将掩码地址转换为32位无符号整型,假设这个数为b。
- 如果此时b为0,则为非法掩码将b按位取反后+1
- 如果此时b为1,则b原来是二进制全1,非法掩码
- 如果b和b-1做按位与运算后为0,则说明是合法掩码,否则为非法掩码
中间还踩到了字符转数字的坑,如果字符串接受到为空,使用stoi直接转化会报错,而我自己写的函数会返回默认的0,这样就判断不出错误了,在调试后发现了这个问题,最后自己写的getNum函数添加了判断接受字符串是否为空的功能。
做这种题目真的很需要耐心下来认真看题目,其实逻辑很清晰。
华为机试刷题记录 文章被收录于专栏
记录一下手打代码的解题思路方便复习


查看16道真题和解析