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:

  1. An integer number followed by an optional exponent.
  2. 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:

  1. Digits followed by a dot ‘.’.
  2. Digits followed by a dot ‘.’ followed by digits.
  3. 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;
}
05-05 12:18