1. IPv6地址压缩问题背景
IPv6地址由8组16位十六进制数组成,每组用冒号分隔,完整格式如2001:0db8:0000:0000:0000:ff00:0042:8329。实际使用中存在大量连续零组,按照RFC 4291规范,可通过以下规则压缩:
- 每组前导零可省略(如
0042→42) - 连续全零组可用双冒号
::替代(仅允许压缩一次)
以题目P2815为例,原始地址2001:0410:0000:0000:0000:ff00:0042:8329经压缩后应为2001:410::ff00:42:8329。手动操作容易出错,特别是处理多个连续零组时可能误判最优压缩位置。
2. 算法设计思路拆解
2.1 标记数组预处理
首先将原始地址按冒号分割为8个段,创建标记数组zeros[8]记录各段是否全零:
python复制segments = address.split(':')
zeros = [1 if seg == '0000' else 0 for seg in segments]
示例输入2001:0410:0000:0000:0000:ff00:0042:8329生成的标记数组为:
[0, 0, 1, 1, 1, 0, 0, 0]
2.2 滑动窗口定位最长零序列
使用双指针滑动窗口算法找出最长连续全零段:
python复制max_len = 0
best_start = -1
left = 0
while left < 8:
if zeros[left] == 1:
right = left
while right < 8 and zeros[right] == 1:
right += 1
current_len = right - left
if current_len > max_len:
max_len = current_len
best_start = left
left = right
else:
left += 1
对于示例数据,将找到从索引2开始长度为3的最长零序列(段2-4)。
2.3 双冒号替换规则
根据RFC规范处理特殊情况:
- 当最长零序列长度相同时,选择最左边的(如段2-4和段3-5都长3时优先选前者)
- 单个零组不压缩(如
2001:0000:1234→2001:0:1234而非2001::1234)
2.4 前导零去除
非压缩段需去除前导零:
python复制def remove_leading_zeros(seg):
seg = seg.lstrip('0')
return seg if seg else '0'
3. 完整实现代码
python复制def compress_ipv6(address):
segments = address.split(':')
if len(segments) != 8:
return "Invalid IPv6 address"
# 标记全零段
zeros = [1 if seg == '0000' else 0 for seg in segments]
# 寻找最长连续零段
max_len = 0
best_start = -1
left = 0
while left < 8:
if zeros[left] == 1:
right = left
while right < 8 and zeros[right] == 1:
right += 1
current_len = right - left
if current_len > max_len:
max_len = current_len
best_start = left
left = right
else:
left += 1
# 构建压缩结果
result = []
i = 0
while i < 8:
if i == best_start and max_len >= 2:
result.append('')
i += max_len
# 确保双冒号只出现一次
if '::' not in ''.join(result):
result.append('')
else:
i -= max_len
result.pop()
result.append(remove_leading_zeros(segments[i]))
i += 1
else:
result.append(remove_leading_zeros(segments[i]))
i += 1
compressed = ':'.join(result)
# 处理双冒号连续情况
compressed = compressed.replace(':::', '::')
return compressed
def remove_leading_zeros(seg):
seg = seg.lstrip('0')
return seg if seg else '0'
4. 边界情况处理
4.1 全零地址
输入0000:0000:0000:0000:0000:0000:0000:0000应输出::
4.2 头尾零段
输入0000:1234:0000:5678:0000:0000:0000:abcd最优解为0:1234:0:5678::abcd
4.3 多个等长零序列
输入2001:0000:0000:1234:0000:0000:5678:0000应选择最左压缩为2001::1234:0:0:5678:0
5. 算法复杂度分析
- 时间复杂度:O(n)线性扫描,n为地址段数(固定为8)
- 空间复杂度:O(n)存储标记数组和结果
- 实际场景中IPv6地址段数恒定,可视为O(1)复杂度
6. 测试用例验证
python复制test_cases = {
'2001:0db8:0000:0000:0000:ff00:0042:8329': '2001:db8::ff00:42:8329',
'0000:0000:0000:0000:0000:0000:0000:0001': '::1',
'2001:0000:0000:1234:0000:0000:5678:0000': '2001::1234:0:0:5678:0',
'0000:1234:0000:5678:0000:0000:0000:abcd': '0:1234:0:5678::abcd'
}
for input_addr, expected in test_cases.items():
assert compress_ipv6(input_addr) == expected, f"Failed: {input_addr}"
7. 实际应用注意事项
- DNS解析兼容性:部分旧系统可能无法正确解析含多个双冒号的地址
- 大小写规范:RFC规定字母推荐小写,但实际实现需保持大小写一致性
- 零压缩冲突:如
2001::1可能被误解析为2001:0:0:0:0:0:0:1或2001:0:0:0:0:0:1:0 - 网络设备差异:某些路由器对压缩地址的日志记录可能展开显示
关键提示:在存储压缩地址时,建议同时保存原始完整地址,避免后续解析歧义
8. 性能优化方向
- 位运算加速:将8个段的零值标记压缩为一个8位二进制数,通过位操作快速定位连续零
- 并行处理:SIMD指令同时处理多个段的前导零去除
- 预处理缓存:对高频出现的地址模式建立压缩结果缓存
9. 同类问题扩展
该算法模式可应用于:
- MAC地址压缩(如
00-1A-2B-00-00-0C→0:1A:2B::C) - 版本号序列简化(
1.0.0.0.0.2→1.0..2) - 时间格式压缩(
2023:00:00:00:00→2023::)
这种标记数组+滑动窗口的组合尤其适合处理具有固定分段结构的数据压缩场景。