题解 | 识别有效的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还是厉害的~