昨天光tm做实验了,写了一堆实验报告,今天上午也是。
所以昨天没写oj题。没写就没写吧(那我不能穿越回昨天233),今天得写了,还是尽量一天一道,循序渐进
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
题目给的代码框架:
class Solution {
public:
bool isNumeric(char* string)
{
}
};
题目分析
-
首先你找出表示数值的字符串似乎很难找,太多了。
-
那就找出不表示数值的字符串呗,用**排除法**
-
怎么排除呢,统计字符串的字符表示什么及其位置呗。对吧,以我的水平只有这种方法行得通。你看上面的题干提供的参考字符串,里面有一些字符的位置是不符合表示数值的字符串的字符位置的,有一些字符不是表示数值的字符
-
所以,我根据题目给的表示or不表示数值的参考字符串,列出以下排除不表示字符串的依据 :(统计’+’ ‘-’ ‘e’ ‘E’ ‘.’ 五个字符出现的次数和位置并筛出一部分明显不表示数值的字符串)
- 1.字符串的字符不是E/e,小数点,0~9,+/-的,则该字符串不表示数值
- 2.字符串是空串,则该字符串不表示数值
- 3.字符串的字符E/e,小数点出现两次以上,则该字符串不表示数值
- 4.字符串的字符+/-未放在紧接E/e(如果有E/e字符)之后或字符串首位,也就是说除了这两个位置可以有+/-字符其他位置如果还有+/-字符的字符串则不表示数值
- 5.小数点放在E/e(如果有E/e字符)之后了,则该字符串不表示数值
- 6.E/e字符之后没有其他字符,则该字符串不表示数值
-
统计’+’ ‘-’ ‘e’ ‘E’ ‘.’ 五个字符出现的次数和第四种情况结合一下,可以有效筛选命中第四种情况的非数值字符串。
- E/e(如果有E/e字符)后紧接+/-字符,+/-字符出现次数自减1
- +/-字符出现在字符串首位,+/-字符出现次数自减1
- 然后如果+/-字符出现次数还不为0,即命中除了这两个位置可以有+/-字符其他位置如果还有+/-字符的字符串的情况,此时该字符串不表示数值,return false
-
结合上面的思路和规则,我们可以编写代码了。(注意先进行异常处理,也就是空串不用判断是否表示数值,肯定不表示,直接返回false即可。)
-
表示数值的字符串返回true,经过下面的5轮筛选的字符串还未出局(未被排除)后才能返回true
-
被筛选后需要排除的字符串,命中哪个排除情况,就在哪个排除情况对应的if语句里写return false;
-
统计次数和位置用for循环遍历字符串即可,因为我要统计的字符位置除+/-字符外都是一锤定音。+/-字符记录次数即可,位置不必记录,也用不到。
-
判断字符串是否需要排除的情况用if语句,关于E/e字符后的字符情况用if+for循环解决
题解代码
class Solution {
public:
bool isNumeric(char* string)
{//排除法
//unsigned int n=string.size();//不能这么写
//线上编译器报错:error: member reference base type 'char *' is not a structure or union
//要写int n=strlen(string);
int n=strlen(string);
if(n==0)
{
return false;//空串,返回否(不表示数值,下同)
}
int cntdat=0,cnteE=0,cntplusminer=0,posdat=0,poseE=0;
//int posplusminer=0;用不到+/-字符的位置。
//error: use of undeclared identifier 'posdat',出现该错误原因:posdat前面是;
//改成英文格式的,
//开始统计'+''-''e''E''.'五个字符出现的数和位置并筛出一部分明显不表示数值的字符串
for(int i=0;i<n;i++)
{
if(string[i]!='e'&&string[i]!='E'&&!(string[i]>='0'&&string[i]<='9')
&&string[i]!='.'&&string[i]!='+'&&string[i]!='-')
{
return false;//出现了除以上字符外的字符均代表该字符串不表示数值,返回false
}
//第一轮筛选完毕。开始统计'+''-''e''E''.'五个字符出现的数和位置
if(string[i]=='.')
{
cntdat++;
posdat=i;
}
if(string[i]=='e'||string[i]=='E')
{
cnteE++;
poseE=i;
}
if(string[i]=='+'||string[i]=='-')//+500样例没过,因为||写成了&&
{
cntplusminer++;
//posplusminer=i;用不到+/-字符的位置。
}
}
//第二轮筛选,E/e和.出现两次及以上,+和-共出现三次及以上的字符串不是表示数值
if(cntdat>=2||cnteE>=2||cntplusminer>=3)
{
return false;
}
//第三轮筛选,.放在了E/e(如果有E/e)之后的字符串不是表示数值
if(cnteE&&posdat>poseE)
{
return false;
}
//第四轮筛选,+/-除放在首位或者紧接E/e(如果有E/e)之后还有其他字符有+/-的(
//体现在cntplusminer在减掉属于这些除。。的情况的统计字符后还不为0)字符串不是表示数值
if(string[0]=='+'||string[0]=='-')
{
cntplusminer--;
}
if(cnteE&&(string[poseE+1]=='+'||string[poseE+1]=='-'))//如果E/e有的话才开始后面的判断
{
cntplusminer--;
}
if(cntplusminer)//cntplusminer在减掉属于这些除。。的情况的统计字符后还不为0
{
return false;
}
//第五轮筛选,E/e(如果有的话)后必须为+/-(必须紧跟在E/e后)或者连续一堆数字,否则该字符串
//不表示数值
if(cnteE)
{
if(poseE==n-1)
{
return false;//比如12e这种情况下没有必要再进行筛选了,这个肯定不是字符串表示数值了
}
//排除了这种情况后进行第五轮第二次筛选
int i=poseE+1;
// error: use of undeclared identifier 'i'
//原因:i之前在for循环的初始条件那里定义了,仅在for循环里有效,for循环之外无效,
//这里的i需要重新声明
if(string[i]=='+'||string[i]=='-')
{
i++;
for(;i<n;i++)
{
if(string[i]<'0'||string[i]>'9')
//只要+/-后面有不是0~9的字符,代表该字符串不表示数值
{
return false;
}
}
}
}
return true;
}
};
小结
排除法巧妙地运用。虽然暴力,但是简单,对萌新很友好。
所以这个算法还是有待优化的,加油,奥里给!