题目描述:
Validate if a given string is numeric.
例子:"0"
=> true
" 0.1 "
=> true
"abc"
=> false
"1 a"
=> false
"2e10"
=> true
注意:It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.
分析:
题意:给定一个字符串,判断它是否为数字。
思路:据说这道题是LeetCode史上最差的题,因为验证规则没有讲清楚,所以好多bad cases很难想到。
如图所示,先给出了数字的整体模式:整体符号(+, 1) | 整型数字(0→9) | 小数点(.) | 小数部分(0→9) | 科学记数符号(e, E) | 科学记数符号(+, 1) | 科学记数数字(0→9),然后给出了一些特殊的例子和判定结果。我总结了以下规则:
① 一定要有数字出现,如果出现科学记数符号e/E,前后都必须有数字。
② 如果出现科学记数符号e/E,前面的数字可以是整型、小数型,后面的数字必须是整型;前后都可以出现数字正负号。
③ 数字正负号的位置必须在第一个位置或者科学记数符号e/E后一个位置;科学记数符号e/E前后,各自最多只能出现一次。
④ 如果出现小数点,且整数部分或者是小数部分是零,可以局部省略(1.0→1.,0.1→.1,但是根据规则①,0.0不能同时省略数字)。
⑤ 小数点和科学记数符号e/E不能重复出现。
这里分别给出了:Ⅰ. 我自己第一遍的AC代码;Ⅱ. 我参考别人思路实现的比较优雅的代码。
假设字符串长度为n,时间复杂度为O(n)。
代码(Ⅰ):
#include <bits/stdc++.h>
using namespace std;
// string
// Accepted
class Solution {
public:
bool isNumber(string s) {
int n = s.length();
// Exceptional Case:
if(n == 0){
return true;
}
// remove the blanks from the left and the right
int start = 0, end = n - 1;
while(start <= n - 1 && s[start] == ' '){
start++;
}
if(start == n){
return false;
}
while(end >= 0 && s[end] == ' '){
end--;
}
// start -> end
int cnt_sign = 0, pos_sign_1 = -1, pos_sign_2 = -1;
int cnt_points = 0, pos_point = -1;
int cnt_es = 0, pos_e = -1;
int cnt_digits = 0, pos_digit = -1;
for(int i = start; i <= end; i++){
if(s[i] == '+' || s[i] == '-'){
cnt_sign++;
if(cnt_sign == 1){
pos_sign_1 = i;
}
else if(cnt_sign == 2){
pos_sign_2 = i;
}
}
else if(s[i] == '.'){
cnt_points++;
pos_point = i;
}
else if(s[i] == 'e'){
cnt_es++;
pos_e = i;
}
else if(s[i] >= '0' && s[i] <= '9'){
cnt_digits++;
if(pos_digit == -1){
pos_digit = i;
}
}
// ' ', 'a', 'b', 'c', 'E'...
else{
return false;
}
}
if(cnt_sign >= 3 || cnt_points >= 2 || cnt_es >= 2){
return false;
}
if(cnt_sign >= 2 && cnt_es == 0){
return false;
}
if(cnt_es == 0){
if(cnt_digits == 0){
return false;
}
if(cnt_sign == 1 && pos_sign_1 != start){
return false;
}
if(cnt_points == 1 && !((pos_point - 1 >= start && s[pos_point - 1] >= '0' && s[pos_point - 1] <= '9') || (pos_point + 1 <= end && s[pos_point + 1] >= '0' && s[pos_point + 1] <= '9'))){
return false;
}
}
else if(cnt_es == 1){
if(cnt_digits == 0 || (cnt_digits >= 1 && pos_digit >= pos_e)){
return false;
}
if(!(s[end] >= '0' && s[end] <= '9')){
return false;
}
if(pos_e == start || pos_e == end){
return false;
}
if(cnt_points == 1 && pos_point >= pos_e){
return false;
}
if(cnt_points == 1 && !((pos_point - 1 >= start && s[pos_point - 1] >= '0' && s[pos_point - 1] <= '9') || (pos_point + 1 <= end && s[pos_point + 1] >= '0' && s[pos_point + 1] <= '9'))){
return false;
}
if(cnt_sign == 1 && !((pos_sign_1 == start) || (pos_sign_1 == pos_e + 1))){
return false;
}
if(cnt_sign == 2 && !((pos_sign_1 == start) && (pos_sign_2 == pos_e + 1))){
return false;
}
}
return true;
}
};
代码(Ⅱ):
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
bool isNumber(string s) {
// trim
s.erase(0, s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
// a couple of flags
bool pointSeen = false;
bool eSeen = false;
bool numberSeen = false;
// bool numberAfterE = true; also correct!
bool numberAfterE = false;
// one pass
int n = s.length();
for(int i = 0; i <= n - 1; i++){
if(s[i] >= '0' && s[i] <= '9'){
numberSeen = true;
numberAfterE = true;
}else if(s[i] == '.'){
if(eSeen || pointSeen){
return false;
}
pointSeen = true;
}else if(s[i] == 'e'){
if(eSeen || !numberSeen){
return false;
}
numberAfterE = false;
eSeen = true;
}else if(s[i] == '+' || s[i] == '-'){
if(i != 0 && s[i - 1] != 'e'){
return false;
}
}else{
return false;
}
}
// get answer
return numberSeen && numberAfterE;
}
};