题目的主要信息:

  • 给出多行字符串,每行都是"IP地址~掩码"的形式,统计A类地址、B类地址、C类地址、D类地址、E类地址、错误IP地址或错误掩码、私有IP的个数,之间以空格隔开

  • IP地址类型如下:

    A类地址1.0.0.0~126.255.255.255;

    B类地址128.0.0.0~191.255.255.255;

    C类地址192.0.0.0~223.255.255.255;

    D类地址224.0.0.0~239.255.255.255;

    E类地址240.0.0.0~255.255.255.255;

  • 私网IP地址如下:

    10.0.0.0-10.255.255.255

    172.16.0.0-172.31.255.255

    192.168.0.0-192.168.255.255

  • 子网掩码为二进制下前面是连续的1,然后全是0,后半部分出现1就是非法,二进制下全是1或者全是0均为非法

  • 类似于【0...】和【127...】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时请忽略

  • IP地址中不能出现非数字,数字不会大于255

方法一:遍历检查

具体做法:

本来这是一个七分类的问题,但是我们先转化成二分类问题,首先检查IP地址及掩码是否合法,再去讨论究竟是ABCDE还是私有。

  1. 遍历字符串,以点或者~为界限分割数字,如果在分割过程中数字部分出现了非数字字符,则不合法;
  2. 如果分割,计算出来的数字大于255,则不合法;
  3. 分割出来的数字,我们存储在数组中,理论上应该是8个数字,如果少于这个数字则不合法;
  4. 检查分割出来的第一个数字是否是0或者127,这两个数字开头的IP地址合法,但是题目要求忽略;
  5. 然后检查掩码,从数组第4位开始(下标0开始),掩码前面应该是全1,后面是全0,因此找到第一个不是255的数,如果所有的数都是255,说明掩码全1,不合法;
  6. 掩码第一个不是255的数如果刚好是11111110(254)、11111100(252)、11111000(248)、11110000(240)、1110000(224)、11000000(191)、10000000(128),也是可以的,我们进入下一位,后面的数必须是全0,否则也是不合法的。

经过上面的步骤我们排除了所有IP地址及掩码不合法的形式,对于这些也不用继续讨论了,统计完不合法跳过就行了。

然后我们检查数组中的前四个数字,是否在ABCDE对应的范围之内,以及是否在私有地址范围之内,几个判断语句就可以搞定。

alt

#include<iostream>
#include<string>
#include<vector>
using namespace std;

int main(){
    vector<int> arr(7, 0); //分别对应题目的7个类别
    string s;
    while(getline(cin, s)){
        int n = s.length();
        vector<int> ips; //记录ip地址的数字
        bool bad = false;
        bool isnum = false;
        int num = 0;
        for(int i = 0; i < n; i++){ //遍历该ip字符串
            if(s[i] == '.' || s[i] == '~'){ //以.或者~分割
                if(isnum){
                    if(num > 255){
                        bad = true; //错误ip,数字不能超过255
                        break;
                    }
                    ips.push_back(num);
                    isnum = false;
                    num = 0;
                }else{
                    arr[5]++; //错误地址
                    bad = true;
                    break;
                }
            }else if(s[i] >= '0' && s[i] <= '9'){
                isnum = true;
                num = num * 10 + s[i] - '0'; //计算数字
            }else{
                arr[5]++;
                isnum = false; //错误地址,数字部分还有非数字
                bad = true;
                break;
            }
        }
        if(isnum)
            ips.push_back(num); //最后一个数字
        if(ips[0] == 0 || ips[0] == 127 || bad) 
            continue; //忽略0或者127开头的地址,错误ip已经统计了过了,可以忽略
        int mask = 4; //查看掩码的数字
        while(mask < 8 && ips[mask] == 255)
            mask++;  //找到掩码第一个不全为1的数
        if(mask == 8){ //掩码全1也是不合法
            arr[5]++;
            continue; 
        }else if(ips[mask] == 254 || ips[mask] == 252 || ips[mask] == 248 || ips[mask] == 240 || ips[mask] == 224 || ips[mask] == 191 || ips[mask] == 128)
            mask++; //各类掩码含1的最后一位
        while(mask < 8 && ips[mask] == 0)
            mask++;
        if(mask != 8){ //掩码后半部分不能有1
            arr[5]++;
            continue;
        }
        if(ips[0] >= 1 && ips[0] <= 126)
            arr[0]++; //A类地址
        else if(ips[0] >= 128 && ips[0] <= 191) 
            arr[1]++; //B类地址
        else if(ips[0] >= 192 && ips[0] <= 223) 
            arr[2]++; //C类地址
        else if(ips[0] >= 224 && ips[0] <= 239) 
            arr[3]++; //D类地址
        else if(ips[0] >= 240 && ips[0] <= 255) 
            arr[4]++; //E类地址
        if(ips[0] == 10) 
            arr[6]++; //私有地址10开头的
        else if(ips[0] == 172 && (ips[1] >= 16 && ips[1] <= 31)) 
            arr[6]++; //私有地址172.16.0.0-172.31.255.255
        else if(ips[0] == 192 && ips[1] == 168) 
            arr[6]++; //私有地址192.168.0.0-192.168.255.255
    }
    for(int i = 0; i < 7; i++){ //输出
        cout << arr[i];
        if(i != 6)
           cout << " ";
    }
    cout << endl;
    return 0;
}

复杂度分析:

  • 时间复杂度:O(n)O(n)nn为字符串长度,遍历整个字符串,后续的循环都是常数时间
  • 空间复杂度:O(1)O(1),记录数字的数组长度为8,常数空间,记录答案的数组长度为7,常数空间

方法二:正则表达式

具体做法:

我们也可以根据题目所给信息写出掩码的正确形式的正则表达式、ABCDE类地址的正则表达式,私有地址的正则表达式。每次将输入的字符串从~处分割成两份,优先判断IP地址开头是否是127或者0,这两种直接忽略,然后验证掩码部分是否合法,再验证私有地址,最后验证五类IP地址,如果不属于5种IP地址,也是不符合。

import re
#各类的地址的正则表达式
error_pattern = re.compile(r'1+0+')
A_pattern = re.compile(r'((12[0-6]|1[0-1]\d|[1-9]\d|[1-9])\.)((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
B_pattern = re.compile(r'(12[8-9]|1[3-8]\d|19[0-1])\.((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
C_pattern = re.compile(r'(19[2-9]|2[0-1]\d|22[0-3])\.((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
D_pattern = re.compile(r'(22[4-9]|23\d)\.((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
E_pattern = re.compile(r'(24\d|25[0-5])\.((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
self_pattern = re.compile(r'((10\.((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d))|(172\.(1[6-9]|2\d|3[0-1])\.(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d))|(192\.168\.(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)))')
escape = re.compile(r'((0|127)\.((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d))')

def judge_mask(line):
    if line == '255.255.255.255' or line == '0.0.0.0': #全0全1
        return 0
    judge = line.split('.') #按照点分割数字
    res = ''
    for j in judge:
        res += '{:0>8b}'.format(int(j))
    res = re.fullmatch(error_pattern, res)
    if res == None:
        return 0
    else:
        return 1

def judge(pattern, line): #匹配函数
    if re.fullmatch(pattern, line) != None:
        return 1
    else:
        return 0

arr = [0]*7
while True:
    try:
        line = input().split('~') #从~分割IP地址和掩码
        if line[0][0:4] == "127." or line[0][0:2] == "0.":  #优先判断127或者0开头的
            continue
        if judge_mask(line[1]): #先判断掩码是否正确
            #正则比较各个IP地址
            if judge(self_pattern, line[0]): #私有与其他的不冲突,优先单独匹配
                arr[6] += 1
            if judge(A_pattern, line[0]):
                arr[0] += 1
            elif judge(B_pattern, line[0]): 
                arr[1] += 1
            elif judge(C_pattern, line[0]):
                arr[2] += 1
            elif judge(D_pattern, line[0]):
                arr[3] += 1
            elif judge(E_pattern, line[0]):
                arr[4] += 1
            elif judge(escape, line[0]):
                continue
            else:
                arr[5] += 1 #IP地址错误
        else: #掩码错误
            arr[5] += 1
    except:
        print(' '.join([str(i) for i in arr])) #输出
        break

复杂度分析:

  • 时间复杂度:O(n)O(n),正则匹配的复杂度
  • 空间复杂度:O(1)O(1),各个临时变量都是常数级