65. Valid Number
Given a string s, return whether s is a valid number.
For example, all the following are valid numbers: “2”, “0089”, “-0.1”, “+3.14”, “4.”, “-.9”, “2e10”, “-90E3”, “3e+7”, “+6e-1”, “53.5e93”, “-123.456e789”, while the following are not valid numbers: “abc”, “1a”, “1e”, “e3”, “99e2.5”, “–6”, “-+3”, “95a54e53”.
Formally, a valid number is defined using one of the following definitions:
- An integer number followed by an optional exponent.
- A decimal number followed by an optional exponent.
An integer number is defined with an optional sign ‘-’ or ‘+’ followed by digits.
A decimal number is defined with an optional sign ‘-’ or ‘+’ followed by one of the following definitions:
- Digits followed by a dot ‘.’.
- Digits followed by a dot ‘.’ followed by digits.
- A dot ‘.’ followed by digits.
An exponent is defined with an exponent notation ‘e’ or ‘E’ followed by an integer number.
The digits are defined as one or more digits.
Example 1:
Example 2:
Example 3:
Constraints:
- 1 <= s.length <= 20
- s consists of only English letters (both uppercase and lowercase), digits (0-9), plus ‘+’, minus ‘-’, or dot ‘.’.
From: LeetCode
Link: 65. Valid Number
Solution:
Ideas:
1. Flag Variables:
- numSeen: This flag indicates whether at least one digit has been encountered in the string. It helps ensure there’s a valid numeric component in the string.
- dotSeen: Tracks if a decimal point has been seen. This prevents multiple decimals which would invalidate the format.
- eSeen: Indicates if an exponent character (e or E) has appeared, ensuring no repeated exponents and that the exponent is properly formatted.
- numberAfterE: After an exponent is seen, this flag checks if a valid number follows. The presence of a number after the exponent is crucial for a valid scientific notation.
2. Initial Whitespace Handling:
- The loop while (*s == ’ ') { s++; } moves the pointer s past any leading white spaces. Although the problem constraints do not mention spaces, handling them might make the function more generally applicable.
3. Character-by-Character Validation:
- The for loop iterates through each character of the string:
Digits: On encountering a digit, it sets numSeen to true and numberAfterE to true, indicating that there’s a valid number component.- Decimal Point (‘.’): Valid only if no previous decimal point or exponent has been seen (dotSeen or eSeen are false).
- Exponent (‘e’ or ‘E’): Valid only if no previous exponent has been seen and there has been at least one digit before it. It sets eSeen to true and expects digits to follow (numberAfterE to false).
- Signs (‘+’ or ‘-’): Signs are valid only at the beginning of the string or immediately following an exponent.
Invalid Characters: Any character that is not a digit, a sign, a decimal point, or an exponent character leads to an immediate return of false.
4. Final Validity Check:
- After exiting the loop, the function checks if at least one number has been seen (numSeen) and if a valid number follows any e or E (numberAfterE). This final validation ensures the overall string represents a valid number format.
5. Edge Case and Error Handling:
- The function is designed to immediately reject strings upon encountering invalid scenarios, making it efficient in terms of runtime, especially for strings that fail early in the validation process.
Code:
bool isNumber(char* s) {
// State flags
bool numSeen = false;
bool dotSeen = false;
bool eSeen = false;
bool numberAfterE = true;
// Trim leading whitespace
while (*s == ' ') {
s++;
}
// Process each character
for (int i = 0; s[i] != '\0'; i++) {
if (isdigit(s[i])) {
numSeen = true;
numberAfterE = true;
} else if (s[i] == '.') {
// There must not be a dot or 'e'/'E' already seen
if (dotSeen || eSeen) {
return false;
}
dotSeen = true;
} else if (s[i] == 'e' || s[i] == 'E') {
// There must not be an 'e'/'E' already seen and there must be a number before it
if (eSeen || !numSeen) {
return false;
}
eSeen = true;
numberAfterE = false;
} else if (s[i] == '+' || s[i] == '-') {
// Sign must come after 'e' or 'E', or be at the start
if (i > 0 && (s[i-1] != 'e' && s[i-1] != 'E')) {
return false;
}
} else {
// Invalid character
return false;
}
}
// Trim trailing whitespace
while (*s == ' ') {
s++;
}
// Valid number if we've seen a number and if after 'e'/'E' there is also a number
return numSeen && numberAfterE;
}