題目描述:
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;
}
};