题解 | 识别有效的IP地址和掩码并进行分类统计
识别有效的IP地址和掩码并进行分类统计
https://www.nowcoder.com/practice/de538edd6f7e4bc3a5689723a7435682
坑爹的题目条件:IP地址如果是0.*.*.*和127.*.*.*都可以不用判定类别和错误,直接跳过。如果这个条件被忽略了就会导致中间有一个测试样例过不了:127.201.56.50~255.255.111.255(这个不用给错误计数+1)
关于判定掩码是否合法的算法:直接一位一位判定有些繁琐,但是用移位操作来判断就简单不少了(我这里用了取反,不取反一样可以判断,我一开始觉得取反后理解方便一些,但是实际上是等价的)。
use std::io::{self, *};
fn main() {
let stdin = io::stdin();
let mut buffer = String::new();
let (mut a, mut b, mut c, mut d, mut e, mut wrong_cnt, mut private_ip) = (0, 0, 0, 0, 0, 0, 0);
while let Ok(n) = stdin.read_line(&mut buffer) {
if n == 0 {
break;
}
let mut inputs = buffer.trim().split('~');
let ip = inputs.next().unwrap();
let mask = inputs.next().unwrap();
let ip_v: Vec<&str> = ip.split('.').collect();
let mask_v: Vec<&str> = mask.split('.').collect();
let mut has_wrong = false;
let mut mask = 0;
let first_seg: u8;
if let Ok(seg) = ip_v[0].parse() {
first_seg = seg;
} else {
wrong_cnt += 1;
has_wrong = true;
buffer.clear();
continue;
}
if first_seg == 127 || first_seg == 0 {
buffer.clear();
continue;
}
for i in 0..4 {
if ip_v[i].is_empty() {
wrong_cnt += 1;
has_wrong = true;
break;
}
if mask_v[i].is_empty() {
wrong_cnt += 1;
has_wrong = true;
break;
} else {
let mask_seg: u8 = mask_v[i].parse().unwrap();
mask += (mask_seg as u32) << ((3 - i) * 8);
}
}
if has_wrong {
buffer.clear();
continue;
}
if mask == 0 || mask == u32::MAX {
wrong_cnt += 1;
buffer.clear();
continue;
}
// 取反,并从低位开始右移到第一个0的位置(原本的最低位1)
// 用了u32,因为不想出现算数移位操作
mask = mask ^ u32::MAX;
while mask % 2 == 1 {
mask >>= 1;
}
// 如果此时这个数字含有1(说明原本的数高位存在0),就是一个非法的掩码
if mask != 0 {
wrong_cnt += 1;
buffer.clear();
continue;
}
if first_seg >= 1 && first_seg < 127 {
a += 1;
} else if first_seg >= 128 && first_seg < 192 {
b += 1;
} else if first_seg >= 192 && first_seg < 224 {
c += 1;
} else if first_seg >= 224 && first_seg < 240 {
d += 1;
} else {
e += 1;
}
if first_seg == 10 {
private_ip += 1;
} else if first_seg == 172 {
let second_seg: u8 = ip_v[1].parse().unwrap();
if second_seg >= 16 && second_seg < 32 {
private_ip += 1;
}
} else if first_seg == 192 {
let second_seg: u8 = ip_v[1].parse().unwrap();
if second_seg == 168 {
private_ip += 1;
}
}
buffer.clear();
}
println!("{} {} {} {} {} {} {}", a, b, c, d, e, wrong_cnt, private_ip);
}
临场写写出来的代码容易存在冗余逻辑,我让AI帮我精简了下代码,如下:
use std::io::{self, BufRead};
fn parse_ip(ip: &str) -> Option<Vec<u8>> {
ip.split('.').map(|s| s.parse().ok()).collect()
}
fn parse_mask(mask: &str) -> Option<u32> {
let mut m = 0u32;
for (i, part) in mask.split('.').enumerate() {
let seg: u8 = part.parse().ok()?;
m |= (seg as u32) << ((3 - i) * 8);
}
// 合法掩码:连续 1 再连续 0
if m == 0 || m == u32::MAX {
return None;
}
let inv = !m;
if inv & (inv + 1) != 0 {
return None;
}
Some(m)
}
fn classify(first: u8) -> Option<usize> {
match first {
1..=126 => Some(0), // A
128..=191 => Some(1), // B
192..=223 => Some(2), // C
224..=239 => Some(3), // D
240..=255 => Some(4), // E
_ => None,
}
}
fn is_private(ip: &[u8]) -> bool {
match ip {
[10, ..] => true,
[172, s, ..] if (16..=31).contains(s) => true,
[192, 168, ..] => true,
_ => false,
}
}
fn main() {
let stdin = io::stdin();
let mut counts = [0; 5];
let mut wrong = 0;
let mut private = 0;
for line in stdin.lock().lines() {
let line = line.unwrap();
if line.is_empty() {
break;
}
let mut parts = line.trim().split('~');
let (ip_str, mask_str) = (parts.next(), parts.next());
let (Some(ip_str), Some(mask_str)) = (ip_str, mask_str) else {
wrong += 1;
continue;
};
let Some(ip) = parse_ip(ip_str) else {
wrong += 1;
continue;
};
if ip[0] == 0 || ip[0] == 127 {
continue;
}
if parse_mask(mask_str).is_none() {
wrong += 1;
continue;
}
if let Some(idx) = classify(ip[0]) {
counts[idx] += 1;
}
if is_private(&ip) {
private += 1;
}
}
println!(
"{} {} {} {} {} {} {}",
counts[0], counts[1], counts[2], counts[3], counts[4], wrong, private
);
}
只能说AI还是厉害的~