之前涉及到IP地址的题有:
- BM74 数字字符串转化成IP地址
- BM22 比较版本号
BM85题目要求:
IPv4:
- 4组十进制数,范围是0-255
- 每组前无“0”
- 分隔符是“.”
IPv6:
- 8组十六进制数,忽略大小写
- 每组前可以有“0”,保证每组由4个字符组成
- 分隔符是“:”
IPv4错误示例:
- 不是4组:254.254.254
- 位数大于3:2541.254.254.254
- 分隔符连续:254.254..254
- 不是十进制数(0-9):254.254.a.254
- 出现前导“0”:254.254.054.254
- 不在0-255的范围:254.254.256.254
IPv6的错误示例:
- 不是8组
- 位数大于4
- 分隔符连续
- 不是十六进制数(0-9,a-f,A-F)
关于前导“0”
- IPv6允许出现前导“0”,即,a:a:0a:a:a:a:a:a
- 允许出现连续“0”,即,a:a:00a:a:a:a:a:a、a:a:000:a:a:a:a:a、a:a:0000:a:a:a:a:a
BM85的测试用例
牛客网的测试用例很水,程序错了都能通过9个。 (IPv4、IPv6没有分隔符连续的用例、IPv4没有前导“0”的用例)
我写的:
- 这种用if判断的题,关键是能考虑所有情况。
- 以分隔符对字符串进行分隔的split()函数:
- c++好像没有split(),需要自己定义。(python有)
- 判断是否是数字:
- 可以用isdigit(ip[i][j]),或者ip[i][j] > '0' && ip[i][j] < '9'
- isdigit(ip[i][j])更保险,还判断了是否为空,保证后面的stoi()能正常运行。
- string转为int
- 用stoi()函数:int num = stoi(ip[i])
- substr()调试很久:
- substr(i,n),表示在位置i截取n个字符
- 不是表示substr(start, end),即,截取[位置start,位置end)。
以下是我的代码:
我的split()函数写得稀烂,根据用例调试很久。模板的split()写得好一些。
class Solution {
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** 验证IP地址* @param IP string字符串 一个IP地址字符串* @return string字符串*/vector<string> split(string str, char c) {vector<string> ans;int start = -1;int end = 0;while (end < str.size()) {if (str[end] == c) { //粗心,不是end == cif(end == str.size() - 1){ans.push_back(str.substr(start + 1, end - start)); ans.push_back("");start = end;}ans.push_back(str.substr(start + 1, end - start - 1)); //"1"是1组,“1.1..1”是4组,“.1.1.1”是4组,“1.1.1.1.”是4组start = end;}else if (end == str.size() - 1) { //调试很久,不然最后一组不能放入ans.push_back(str.substr(start + 1, end - start)); start = end;}end++;}return ans;}bool isIPv4(string IP) {vector<string> ip = split(IP, '.');//必须有4组if (ip.size() != 4)return false;//每组进行分析for (int i = 0; i < 4; i++) {//每组不为空,且最多有3个字符if (ip[i].size() == 0 || ip[i].size() > 3) return false;//不能有前导“0”if(ip[i].size()>1 && ip[i][0] == '0')return false;//必须是十进制字符for (int j = 0; j < ip[i].size(); j++)if (!((ip[i][j] >= '0' && ip[i][j] <= '9')))return false;//必须在0-255范围int num = stoi(ip[i]); //调试,ip[i]必须是数字,必须【非空】。if (num > 255) return false;}return true;}bool isIPv6(string IP) {vector<string> ip = split(IP, ':');//必须有8组if (ip.size() != 8)return false;//每组进行分析for (int i = 0; i < 8; i++) {//每组不为空,且最多有4个字符if (ip[i].size() == 0 || ip[i].size() > 4)return false;//必须是十六进制字符for (int j = 0; j < ip[i].size(); j++)if (!((ip[i][j] >= '0' && ip[i][j] <= '9') ||(ip[i][j] >= 'a' && ip[i][j] <= 'f') ||(ip[i][j] >= 'A' && ip[i][j] <= 'F'))){ //粗心,忘记加等号“=”。cout<<ip[i][j]<<endl;;return false;}}return true;}string solve(string IP) {// write code herebool flag1 = isIPv4(IP);bool flag2 = isIPv6(IP);if (flag1)return "IPv4";if (flag2)return "IPv6";return "Neither";}
};
模板的:
模板的split(),在while循环中,会把截取的字符串去掉,所以每次只需要截取前n个即可。
vector<string> split(string str, char c) {vector<string> ans;int end;while((end = str.find(c)) && end != str.npos){ans.push_back(str.substr(0,end));str = str.substr(end+1);}ans.push_back(str); //易粗心漏掉return ans;}
一些关键点:
1. string::npos
- string::type_size 类型,也就是find()返回的类型。
- 定义
- 对于size_t npos = -1,-1为size_t的最大值。
- 表示返回值,表示没有匹配项。
- 例如,定义size_t index = str.find("abc"),再判断if(index == string::npos)
- 作为长度参数,表示直到字符串结束。
- 例如,str1.replace(index, string::npos, str2)。
- 注:建议直接使用if(str.find("abc") == string::npos)。
- 因为如果先定义int index = str.find("abc") ,再判断if(index == string::npos),比较结果可能为false。
C++中std::string::npos的用法_C 语言_脚本之家 (jb51.net)
C++中string::npos的用法总结-CSDN博客
2. while((赋值语句))
- 先执行赋值语句,然后对左值进行判断。如果左值为0,则while退出;否则,while继续循环。
- 再加一个括号(),告知编译器:赋值操作符“=”是对的,而不是判断符“==”的误写。
- while((c = *p ++)) // 读取字符串中的字符,直到字符串结尾。具体步骤是:先把*p赋值给c,然后执行指针移位,再判断c的值。如果c值为0,代表字符串结束,退出循环。
- while(b=a,b-=1) // 依次运行,以最后的一段作为结果。
C语言中,while()语句括号内可以是赋值语句吗? (sogou.com)
此外,刷到i++和++i的区别:
C++在使用while循环时关于i++和++i的注意事项_while是不是只能用i++-CSDN博客