""" Solution By: Reniz Shah Topic: Deterministic Finite Automaton (DFA) Given a string s, return whether s is a valid number or not Leetcode link: https://leetcode.com/problems/valid-number/description/ """ from enum import Enum from typing import Dict class CharType(Enum): NUMERIC = "NUMERIC" SIGN = "SIGN" EXPONENT = "EXPONENT" DECIMAL = "DECIMAL" class State(Enum): INITIAL = "INITIAL" SIGNED = "SIGNED" WHOLE = "WHOLE" FRACTIONAL = "FRACTIONAL" FRACTION = "FRACTION" EXPONENTIAL = "EXPONENTIAL" EXP_SIGN = "EXP_SIGN" EXP_NUMBER = "EXP_NUMBER" state_machine: Dict[State, Dict[CharType, State]] = { State.INITIAL: { CharType.NUMERIC: State.WHOLE, CharType.SIGN: State.SIGNED, CharType.DECIMAL: State.FRACTIONAL, }, State.SIGNED: {CharType.NUMERIC: State.WHOLE, CharType.DECIMAL: State.FRACTIONAL}, State.WHOLE: { CharType.NUMERIC: State.WHOLE, CharType.DECIMAL: State.FRACTION, CharType.EXPONENT: State.EXPONENTIAL, }, State.FRACTIONAL: {CharType.NUMERIC: State.FRACTION}, State.FRACTION: { CharType.NUMERIC: State.FRACTION, CharType.EXPONENT: State.EXPONENTIAL, }, State.EXPONENTIAL: { CharType.NUMERIC: State.EXP_NUMBER, CharType.SIGN: State.EXP_SIGN, }, State.EXP_SIGN: {CharType.NUMERIC: State.EXP_NUMBER}, State.EXP_NUMBER: {CharType.NUMERIC: State.EXP_NUMBER}, } from enum import Enum class CharType(Enum): NUMERIC = "NUMERIC" SIGN = "SIGN" EXPONENT = "EXPONENT" DECIMAL = "DECIMAL" class State(Enum): INITIAL = "INITIAL" SIGNED = "SIGNED" WHOLE = "WHOLE" FRACTIONAL = "FRACTIONAL" FRACTION = "FRACTION" EXPONENTIAL = "EXPONENTIAL" EXP_SIGN = "EXP_SIGN" EXP_NUMBER = "EXP_NUMBER" state_machine: dict[State, dict[CharType, State]] = { State.INITIAL: { CharType.NUMERIC: State.WHOLE, CharType.SIGN: State.SIGNED, CharType.DECIMAL: State.FRACTIONAL, }, State.SIGNED: {CharType.NUMERIC: State.WHOLE, CharType.DECIMAL: State.FRACTIONAL}, State.WHOLE: { CharType.NUMERIC: State.WHOLE, CharType.DECIMAL: State.FRACTION, CharType.EXPONENT: State.EXPONENTIAL, }, State.FRACTIONAL: {CharType.NUMERIC: State.FRACTION}, State.FRACTION: { CharType.NUMERIC: State.FRACTION, CharType.EXPONENT: State.EXPONENTIAL, }, State.EXPONENTIAL: { CharType.NUMERIC: State.EXP_NUMBER, CharType.SIGN: State.EXP_SIGN, }, State.EXP_SIGN: {CharType.NUMERIC: State.EXP_NUMBER}, State.EXP_NUMBER: {CharType.NUMERIC: State.EXP_NUMBER}, } def classify_char(char: str) -> CharType | None: """ Classifies a character into one of the following categories: - 'CharType.NUMERIC': if the character is a digit (0-9) - 'CharType.SIGN': if the character is a plus sign (+) or a minus sign (-) - 'CharType.EXPONENT': if the character is an 'e' or 'E' (used in exponential notation) - 'CharType.DECIMAL': if the character is a decimal point (.) - None: if the character does not fit into any of the above categories - None: if size of char is not 1 Parameters: char (str): The character to be classified Returns: CharType: The classification of the character >>> classify_char('2') >>> classify_char('-') >>> classify_char('e') >>> classify_char('.') >>> classify_char('') >>> classify_char('0') >>> classify_char('01') """ if len(char) != 1: return None if char.isdigit(): return CharType.NUMERIC if char in "+-": return CharType.SIGN if char in "eE": return CharType.EXPONENT if char == ".": return CharType.DECIMAL return None def is_valid_number(number_string: str) -> bool: """ This function checks if the input string represents a valid number. It uses a finite state machine to parse the input string, transitioning between states based on the character type. The function returns True if the input string represents a valid number, and False otherwise. A valid number is defined as a string that can be parsed into an integer, decimal, or exponent. >>> is_valid_number("2") True >>> is_valid_number("0089") True >>> is_valid_number("-0.1") True >>> is_valid_number("+3.14") True >>> is_valid_number("4.") True >>> is_valid_number("-.9") True >>> is_valid_number("2e10") True >>> is_valid_number("-90E3") True >>> is_valid_number("3e+7") True >>> is_valid_number("+6e-1") True >>> is_valid_number("53.5e93") True >>> is_valid_number("-123.456e789") True >>> is_valid_number("abc") False >>> is_valid_number("1a") False >>> is_valid_number("1e") False >>> is_valid_number("e3") False >>> is_valid_number("99e2.5") False >>> is_valid_number("--6") False >>> is_valid_number("-+3") False >>> is_valid_number("95a54e53") False >>> is_valid_number(".") False """ valid_final_states = {State.WHOLE, State.FRACTION, State.EXP_NUMBER} current_state = State.INITIAL for char in number_string: char_type = classify_char(char) if char_type is None or char_type not in state_machine[current_state]: return False current_state = state_machine[current_state][char_type] return current_state in valid_final_states if __name__ == "__main__": import doctest doctest.testmod()