前言
我在考试2.17那天,进度只来得及推到HJ18IP地址那个题。因为我花费了将近两天去了解,熟悉动态规划,进度就不太快,加上从16开始,直到20题都是中等或困难,于我而言,这些题我都需要仔细琢磨的。
复习前几天还焦虑的睡不着,后面就被这个强度打败,晚上回去都看不动了,很快就睡着。因为鄙人身体一向不好,精力不足,学生时代也饱受困扰,成绩均不理想,这波就是不服气又想冲一把。
我的结果是out,hr说300分。
考试当天三个题,读题就废了好大功夫,第一题我懵住好一会儿,那个猜字谜感觉要四个循环嵌套才行,然后在纸上比划时候突然想到阿斯码,第一题做完我信心满满,结果一提交用例就通过83.3%,我不服气,硬要找原因,后面时间浪费严重。
因为我其实是准备放弃第三题的嘛,前面读题感觉超级复杂。第二题是啥给忘了我感觉还算简单,但是还是不能通过全部用例,裂开,有了前面浪费时间的经验我就不管了,直接推第三题。结果看着看着,我画完表格分析感觉自己有思路,可惜没时间,就简单写了个最基础的东西,能通过3%用例。
有些可惜结果不好,但是我不后悔这次尝试,感觉收获很多,以及转变了自己的思想。以前觉得算法只会涉及到复杂的问题如背包等,我不会,也用不到就不想刷。但这波我背包搞懂了,而且发现算法题也有对新手友好的。然后我觉得我可以恶补基础,多写代码,重学数据结构、高数线代、英语等课程,高数英语我大学学的时候感觉理解不透彻,没关系,我从高中开始补,从哪里跌倒就从哪里爬起来。
一、描述
描述
请解析IP地址和对应的掩码,进行分类识别。要求按照A/B/C/D/E类地址归类,不合法的地址和掩码单独归类。
所有的IP地址划分为 A,B,C,D,E五类
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。(例如:255.255.255.32就是一个非法的掩码)
(注意二进制下全是1或者全是0均为非法子网掩码)
注意:
- 类似于【0...】和【127...】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时请忽略
- 私有IP地址和A,B,C,D,E类地址是不冲突的
输入描述:
多行字符串。每行一个IP地址和掩码,用~隔开。
请参考帖子https://www.nowcoder.com/discuss/276处理循环输入的问题。
输出描述:
统计A、B、C、D、E、错误IP地址或错误掩码、私有IP的个数,之间以空格隔开。
示例1
输入:
10.70.44.68~255.254.255.0
1.0.0.1~255.0.0.0
192.168.0.2~255.255.255.0
19…0.~255.255.255.0
输出:
1 0 1 0 0 2 1
说明:
10.70.44.68255.254.255.0的子网掩码非法,19…0.255.255.255.0的IP地址非法,所以错误IP地址或错误掩码的计数为2;
1.0.0.1~255.0.0.0是无误的A类地址;
192.168.0.2~255.255.255.0是无误的C类地址且是私有IP;
所以最终的结果为1 0 1 0 0 2 1
示例2
输入:
0.201.56.50~255.255.111.255
127.201.56.50~255.255.111.255
输出:
0 0 0 0 0 0 0
说明:
类似于【0...】和【127...】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时请忽略
二、代码与分析
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HJ17_IPAddr
{
class Program
{
/*
* 计算有效的IP地址与子网掩码
*/
public static Dictionary<string, string> dic = new Dictionary<string, string>();
public static Dictionary<string, int> res = new Dictionary<string, int>();
public static void Main(string[] args)
{
string line;
// 结果数组初始化
res.Add("A", 0);
res.Add("B", 0);
res.Add("C", 0);
res.Add("D", 0);
res.Add("E", 0);
res.Add("error", 0);
res.Add("private", 0);
while (!string.IsNullOrWhiteSpace((line = System.Console.ReadLine())))
{
try
{
// 注意 while 处理多个 case
string[] tokens = line.Split('~');
// 先读取IP与子网掩码
if (!dic.ContainsKey(tokens[0]))
{
dic.Add(tokens[0], tokens[1]);
}
//System.Console.WriteLine(tokens[0] + tokens[1]);
// 读取完毕,开始判断
// 用res放不同的key存储各个数量
// 先判断IP
if (checkIP(tokens[0]))
{
string ip1 = tokens[0].Split('.')[0];
if (int.Parse(ip1) == 0 || int.Parse(ip1) == 127)
{
// 开头0与127忽略
continue;
}
// 检验掩码
if (checkYan(tokens[1]))
{
// ip合法,掩码合法
// 先判断私有
if (checkPrivate(tokens[0]))
{
if (res.ContainsKey("private"))
{
res["private"] += 1;
}
else
{
res.Add("private", 1);
}
}
// A类
int first = int.Parse(tokens[0].Split('.')[0]);
if (first >= 1 && first <= 126)
{
if (res.ContainsKey("A"))
{
res["A"] += 1;
}
else
{
res.Add("A", 1);
}
}
// B类
if (first >= 128 && first <= 191)
{
if (res.ContainsKey("B"))
{
res["B"] += 1;
}
else
{
res.Add("B", 1);
}
}
// C类
if (first >= 192 && first <= 223)
{
if (res.ContainsKey("C"))
{
res["C"] += 1;
}
else
{
res.Add("C", 1);
}
}
// D类
if (first >= 224 && first <= 239)
{
if (res.ContainsKey("D"))
{
res["D"] += 1;
}
else
{
res.Add("D", 1);
}
}
// E类
if (first >= 240 && first <= 255)
{
if (res.ContainsKey("E"))
{
res["E"] += 1;
}
else
{
res.Add("E", 1);
}
}
}
else
{
// 记录掩码不合法
if (res.ContainsKey("error"))
{
res["error"] += 1;
}
else
{
res.Add("error", 1);
}
}
}
else
{
// 记录IP不合法
if (res.ContainsKey("error"))
{
res["error"] += 1;
}
else
{
res.Add("error", 1);
}
}
}
catch (Exception e)
{
System.Console.WriteLine(e.Message);
}
}
Console.Write(res["A"] + " " + res["B"] + " " + res["C"] + " " + res["D"] + " " + res["E"] + " " + res["error"] + " " + res["private"] + " ");
Console.WriteLine();
Console.ReadLine();
}
// 检验IP
public static bool checkIP(string IPstr)
{
// 合法返回true,不合法返回false
string[] IP = IPstr.Split('.');
if (IP.Length != 4)
{
// 不合法,长度不够
return false;
}
foreach (string ss in IP)
{
if (string.IsNullOrWhiteSpace(ss))
{
// 不合法,对应位置为空
return false;
}
else if (int.Parse(ss) < 0 || int.Parse(ss) > 255)
{
//超出范围
return false;
}
}
return true;
}
// 检验私有
public static bool checkPrivate(string IPstr)
{
string[] IP = IPstr.Split('.');
// 第一类私有
if (int.Parse(IP[0]) == 10)
{
return true;
}
// 第二类私有
if (int.Parse(IP[0]) == 172 && int.Parse(IP[1]) <= 31 && int.Parse(IP[1]) >= 16)
{
return true;
}
// 第三类私有
if (int.Parse(IP[0]) == 192 && int.Parse(IP[1]) == 168)
{
return true;
}
return false;
}
// 检验掩码
public static bool checkYan(string Yanstr)
{
if (!checkIP(Yanstr))
{
// 掩码也是ip,要先符合ip规则
return false;
}
// 全1或全0返回false
if (Yanstr == "255.255.255.255" || Yanstr == "0.0.0.0")
{
return false;
}
string[] yan = Yanstr.Split('.');
string[] NewYan = new string[yan.Length];
// 依次转二进制,并按照8位高位补零
for (int i = 0; i < yan.Length; i++)
{
NewYan[i] = Convert.ToString(int.Parse(yan[i]), 2);
if (NewYan[i].Length < 8)
{
NewYan[i] = NewYan[i].PadLeft(8, '0');
}
}
// 拼接起来查找最后一个1和第一个0的位置,不符合的一律非法
string last = string.Join("", NewYan);
int addrlast1 = last.LastIndexOf('1');
int addrFirst0 = last.IndexOf('0');
if (addrlast1 + 1 != addrFirst0)
{
return false;
}
else
{
return true;
}
}
}
}
三、总结
1.这道题呢,不是那种费脑子得难,而是考验模拟流程,繁琐过程设计精细。于我而言,我的计算机网络掌握的不太好,看了官方解题我才发现,子网掩码它首先也得是个合法IP,然后再判断。以及题目给出的0和127开头后面*原来是泛指,我以前那个位置没有数字是字符然后不合法呢,这个以后记住以下。
2.代码流程:主方法循环读取字符串并判断,检验IP时候注意处理空或长度不够,以及校验每一位数字,不可以<0,不可以>255。
3.检验完ip,true继续往下,忽略0和127,false记录错误数,检验掩码注意掩码也首先是合法ip,然后排除全一与全零,拆分每个字符转二进制并八位补零。补完了查找最后一个1位置与第一个0位置,校验关系可得合不合法。
4.掩码为true往下走,false记录错误数量,接下来先校验私有ip与ABCD类,这个没啥说的就按照要求写即可。需要注意的是,我刚开始精力不足,校验ip出了些小失误,今天精力好了就完全解决了。
5.用到了dictionary,split,join,padleft,indexof,lastindexof,还有个要注意的要是全都不合法结果字典就没东西了,所以初始化了我都给赋值0.