Part1. 我的思路和代码(通过92.31%)
这道题我并没有自己做出来
,而是先看了书上的前一道题(也是正则表达式匹配),然后再做,而且做的时候依然是跟着书上的思路才基本做出,最后对空格的小处理为自己的思路
(如Part2所述)。
整体思路
上,先扫描小数点前的有符号整数部分,扫描完后若接下来为小数点,则再扫描一个无符号整数,扫描完后若接下来为e/E,则再扫描一个有符号整数。
代码中处理小数点的分支,使用了或逻辑,这包括4种情况
:(1)小数点前没有整数部分,ans先为false,进一步扫描后小数点后也没有整数,扫描结果为false,或运算后为false,也就是最终结果,如".“;(2)小数点前没有整数部分,ans先为false,进一步扫描后小数点后有整数,扫描结果为true,或运算后为true,比如”.123";(3)小数点前有整数部分,ans先为true,进一步扫描后小数点没有整数,扫描结果为false,或运算后为true,比如"123.“;(4)小数点前有整数部分,ans先为true,进一步扫描后小数点后有整数,扫描结果为true,或运算后为true,比如"123.456”。
代码中处理e/E的分支,使用了与逻辑,包括3种情况
:(1)之前ans为false,说明e之前没有数字,无论e之后如何,最终结果均为false,比如"e789"、“e”;(2)之前ans为true,说明e之前有数字,进一步扫描后结果为false,即e之后没有数字,与运算结果为false,比如"123.456e";(3)之前ans为true,说明e之前有数字,进一步扫描后结果为true,即e之后有数字,与运算结果为true,比如"123.456e789"。
代码行数不多,但包含的情况非常多……
bool scanUnsignedInt(int &idx, const string &str)
{
int beforeIdx = idx;
while(idx<str.length() && '0'<=str[idx] && str[idx]<='9'){
++idx;
}
return idx>beforeIdx;
}
bool scanInt(int &idx, const string &str)
{
if(str[idx]=='+' || str[idx]=='-'){
++idx;
}
return scanUnsignedInt(idx, str);
}
bool isNumeric(string str)
{
bool ans;
int idx;
if(str.length() == 0){
return false;
}
idx = 0;
ans = scanInt(idx, str);
if(str[idx] == '.'){
++idx;
ans = scanUnsignedInt(idx, str) || ans; //注意由于短路逻辑,ans不能写到前面,否则ans为true时,不会进入函数执行,也就不会更新idx,导致之后的匹配过程出错
}
if(str[idx]=='e' || str[idx]=='E'){
++idx;
ans = scanInt(idx, str) && ans;
}
return ans && (idx==str.length());
}
特别记录:需要注意短路逻辑,对于||逻辑,执行到为true的条件就不继续向后执行了,对于&&逻辑,执行到为false的条件就不继续向后执行了。
Part2. 我的思路和代码(通过100%)
我做题的平台是牛客网,该平台此题目和书上不同的地方主要在于存在空格。若不处理空格,则无法通过所有用例,若考虑空格,空格可能出现的位置如下图所示:
在科学计数法中,空格可能出现在e之前的整数或小数部分和e之后的整数部分(首尾部分的蓝色和中间的红色)、整个数的首尾(绿色),如图中举的例子。在经过长时间的思考后,打算目前放弃较为完美的解题,只先处理整个数首、尾部分的空格
,但最终通过了所有用例。具体做法是用循环,分别从首、尾向后、向前扫描字符串,扫描到非空格字符为止。
bool scanUnsignedInt(int &idx, const string &str)
{
int beforeIdx = idx;
while(idx<str.length() && '0'<=str[idx] && str[idx]<='9'){
++idx;
}
return idx>beforeIdx;
}
bool scanInt(int &idx, const string &str)
{
if(str[idx]=='+' || str[idx]=='-'){
++idx;
}
return scanUnsignedInt(idx, str);
}
bool isNumeric(string str)
{
bool ans;
int idx;
int idx_s;
int idx_e;
for(idx_s=0; idx_s<str.length(); idx_s++){ //处理str两端的空格
if(str[idx_s] != ' '){
break;
}
}
for(idx_e=str.length()-1; idx_e>=0; idx_e--){
if(str[idx_e] != ' '){
break;
}
}
str = std::string(str, idx_s, idx_e-idx_s+1);
if(str.length() == 0){
return false;
}
idx = 0;
ans = scanInt(idx, str);
if(str[idx] == '.'){
++idx;
ans = scanUnsignedInt(idx, str) || ans; //注意由于短路逻辑,ans不能写到前面,否则ans为true时,不会进入函数执行,也就不会更新idx,导致之后的匹配过程出错
}
if(str[idx]=='e' || str[idx]=='E'){
++idx;
ans = scanInt(idx, str) && ans;
}
return ans && (idx==str.length());
}
Part3. 心得体会
- 通过这道题,我深刻感受到了
字符串处理
、正则表达式匹配
类型题目的难度,做这道题之前,首先看了书上一道相似的正则表达式题目,但该题的情况更加复杂。虽然不是完全由自己做出来,但也是一种经验的积累; - 要注意
语言特性
的影响,例如短路逻辑; - 努力的同时需要
注意全局
,这道题做了好几天还没有做出,对我而言说明要寻求答案帮助,作为经验积累下来,不能因此独自死磕。