CCF-201812-3-CIDR合并

这题看起来很可怕,考证的时候望而却步。过了好久,直到现在才做题。仔细研究下,发现原来不难,真的是心态爆炸了。

题目中的概念:(其实静下心来看,题目很好理解的。。可能是考试时心态崩了。。。)

ipv4地址:一共32位,分为4个十进制数表示。

ip前缀:<(ip地址) / (约掉IP地址(二进制表示)后面的0之后ip地址的长度,题目称前缀长度)>
ip前缀列表:一列ip前缀呗。

匹配
得到ip前缀的前缀长度len
ip地址的前len长的地址 == ip前缀的前len长的地址 就是匹配(就是前面长得一样)
所以一个ip前缀可以匹配到1-n个的ip地址,比如 255.255.255.0 / 24 就可以匹配到 255.255.255.0-255.255.255.255之间的255(2^8)个ip地址。

等价
两个前缀列表的匹配ip地址集合一样,就是说他们等价。

题目就是让我们找与输入的前缀列表等价的列长最小的前缀列表。

ip前缀的各种缩写,大家看题目就好了,应该不难。

难点
处理缩写,处理合并。
这里只排序就有40分了。但是我只排序的代码拿了60分。
然后合并的方法,题目也教我们了,就差我们动手了。。。

细节:
从小到大合并:遍历列表,a与b合并,直接看b的前缀(按a的前缀长度算)是否和a的前缀一样;下标不用变,不能合并才下标加一。
同级合并:不同级就下标加一,同级不能合并下标加一,可以合并,看列表前面有没有ip前缀,有就下标减一,没有就不变。

python代码:

def minmax_merge():
    """从大到小合并"""
    idx = 0
    while idx < len(pref_sets) - 1:
        first_pref, second_pref = pref_sets[idx], pref_sets[idx + 1]
        first_bin, second_bin = ip2bin(first_pref[:-1]), ip2bin(second_pref[:-1])
        minlen = first_pref[-1]
        if first_bin[:minlen] == second_bin[:minlen]:
            pref_sets.pop(idx + 1)
        else:
            idx += 1


def level_merge():
    """同级合并"""
    idx = 0
    while idx < len(pref_sets) - 1:
        first_pref, second_pref = pref_sets[idx], pref_sets[idx + 1]
        first_bin, second_bin = ip2bin(first_pref[:-1]), ip2bin(second_pref[:-1])
        first_len, second_len = first_pref[-1], second_pref[-1]
        if first_len == second_len:
            l = first_len - 1
            if first_bin[:l] == second_bin[:l]:
                new_pref = bin2ip(first_bin[:l].ljust(32, '0')) + [l]
                pref_sets.pop(idx)
                pref_sets.pop(idx)
                pref_sets.insert(idx, new_pref)
                if idx - 1 >= 0:
                    idx -= 1
            else:
                idx += 1
        else:
            idx += 1


def ip2bin(ip):
    """十进制转二进制"""
    binstr = ''
    for e in ip:
        binstr += (bin(e)[2:]).rjust(8, '0')
    return binstr


def bin2ip(binstr):
    """二进制转十进制"""
    ip = []
    for i in range(0, len(binstr), 8):
        ip.append(int(binstr[i:i + 8], 2))
    return ip


n = int(input())
pref_sets = []  # 前缀列表
for i in range(n):
    pref = input().split('/')  # ip前缀。按斜杆分成ip地址和len
    ip = [int(e) for e in pref[0].split('.')]  # ip地址,按点分开
    l = int(pref[1]) if len(pref) == 2 else len(ip) * 8  # 求len,如果前面的pref长度为2说明输入的前缀列表没有省略长度,有pref[1],直接取整;否则用ip地址的段数*8(题目有说明)
    std_ip = [0] * 4  # 标准ip地址
    for j, e in enumerate(ip):
        std_ip[j] = e  # 遍历ip,给ip地址赋值
    std_pref = std_ip + [l]  # 加上了len,变成标准ip前缀
    pref_sets.append(std_pref)  # 添加进前缀列表
pref_sets = sorted(pref_sets)
minmax_merge()
level_merge()
for p in pref_sets:
    print('{}.{}.{}.{}/{}'.format(p[0], p[1], p[2], p[3], p[4]))

# 2
# 2
# 1

# 3
# 0/1
# 2/1
# 128/1

# 3
# 101.6.6.0/24
# 101.6.8.0/24
# 101.6.9.0/24

python代码(没合并的60分代码):
是不是很简单。。。没做真的亏了,十几行就可以拿60分了。

n = int(input())
pref_sets = []  # 前缀列表
for i in range(n):
    pref = input().split('/')  # ip前缀。按斜杆分成ip地址和len
    ip = [int(e) for e in pref[0].split('.')]  # ip地址,按点分开
    l = int(pref[1]) if len(pref) == 2 else len(ip) * 8  # 求len,如果前面的pref长度为2说明输入的前缀列表没有省略长度,有pref[1],直接取整;否则用ip地址的段数*8(题目有说明)
    std_ip = [0] * 4  # 标准ip地址
    for j, e in enumerate(ip):
        std_ip[j] = e  # 遍历ip,给ip地址赋值
    std_pref = std_ip + [l]  # 加上了len,变成标准ip前缀
    pref_sets.append(std_pref)  # 添加进前缀列表
pref_sets = sorted(pref_sets)
for p in pref_sets:
    print('{}.{}.{}.{}/{}'.format(p[0], p[1], p[2], p[3], p[4]))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值