之前涉及到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 == c
if(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 here
bool 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)
2. while((赋值语句))
- 先执行赋值语句,然后对左值进行判断。如果左值为0,则while退出;否则,while继续循环。
- 再加一个括号(),告知编译器:赋值操作符“=”是对的,而不是判断符“==”的误写。
- while((c = *p ++)) // 读取字符串中的字符,直到字符串结尾。具体步骤是:先把*p赋值给c,然后执行指针移位,再判断c的值。如果c值为0,代表字符串结束,退出循环。
- while(b=a,b-=1) // 依次运行,以最后的一段作为结果。
C语言中,while()语句括号内可以是赋值语句吗? (sogou.com)
此外,刷到i++和++i的区别: