65.有效数字
标签: string
难度: Hard
通过率: 20.75%
原题链接: https://leetcode.com/problems/valid-number/description/
题目描述
给定一个字符串 ,返回 是否是一个有效数字。有效数字的定义如下:
- 表示一个整数的数字可以后接一个可选指数。
- 一个十进制数字可以后接一个可选指数 。
整数定义为一个可选的符号 '-' 或 '+',紧跟随的必须是一串数字。
十进制数字定义为一个可选的符号 '-' 或 '+',并遵循以下定义之一:
- 数字后面跟随一个点 '.'。
- 数字后面跟随一个点 '.',后面再跟随数字。
- 一个点 '.' 后面直接跟随数字。
一个指数定义为指数符号 'e' 或 'E' 后跟一个整数。
数字定义为一个或多个数字。
解题思路
解决这个问题的主要思想是使用有限状态机(Finite State Machine, FSM)来验证字符串是否是一个有效数字。你需要逐个字符检查,并根据出现的字符转换状态。通过实现一个状态转换的完整流程,决定该字符串是否是一个有效数字。
以下是状态转换机的解释:
- 初始化状态,正在处理开头的符号。
- 正在读取整数或小数之前的部分数字。
- 确定小数点的位置并处理小数部分的数字输入。
- 处理科学计数法中的指数符号。
- 读取指数部分的数字。
- 提取任何后的无效字符。
根据对字符串进行遍历的过程来调整状态,最后检查状态是否能够保证字符串是一个有效数字。
代码实现
- Python
- C++
- JavaScript
- Java
def isNumber(s: str) -> bool:
    # 定义状态机的状态集合
    state = [
        {},  # dummy state 0
        {'blank': 1, 'sign': 2, 'digit': 3, '.': 4},  # state 1: start, leading whitespace allowed
        {'digit': 3, '.': 4},  # state 2: sign
        {'digit': 3, '.': 5, 'e': 6, 'blank': 9},  # state 3: digit (integer part)
        {'digit': 5},  # state 4: dot has been met, need at least one digit after it
        {'digit': 5, 'e': 6, 'blank': 9},  # state 5: digit (fractional part)
        {'sign': 7, 'digit': 8},  # state 6: e or E met
        {'digit': 8},  # state 7: sign after e
        {'digit': 8, 'blank': 9},  # state 8: digit after e
        {'blank': 9}  # state 9: trailing whitespaces allowed
    ]
    current_state = 1
    for char in s:
        if char.isdigit():
            transition = 'digit'
        elif char in '+-':
            transition = 'sign'
        elif char in 'eE':
            transition = 'e'
        elif char == '.':
            transition = '.'
        elif char == ' ':
            transition = 'blank'
        else:
            transition = '?'  # illegal character
        # Check the transition
        if transition not in state[current_state]:
            return False
        # Change to next state
        current_state = state[current_state][transition]
    # Accepting states: 3, 5, 8, 9
    return current_state in [3, 5, 8, 9]
class Solution {
public:
    bool isNumber(string s) {
        enum State {
            START,
            SIGN,
            INTEGER,
            DOT,
            DECIMAL,
            EXPONENT_SIGN,
            EXPONENT,
            END
        };
        State state = START;
        bool seenDigit = false;
        bool seenExponent = false;
        bool seenDot = false;
        for (const char &ch : s) {
            if (isdigit(ch)) {
                seenDigit = true;
                if (state == START || state == SIGN || state == INTEGER) {
                    state = INTEGER;
                } else if (state == DOT || state == DECIMAL) {
                    state = DECIMAL;
                } else if (state == EXPONENT_SIGN || state == EXPONENT)
                    state = EXPONENT;
            } else if (ch == '+' || ch == '-') {
                if (state == START) {
                    state = SIGN;
                } else if (state == EXPONENT && !seenDigit)
                    state = EXPONENT_SIGN;
                else
                    return false;
            } else if (ch == '.') {
                if ((state == START || state == SIGN || state == INTEGER) && !seenDot) {
                    state = DOT;
                    seenDot = true;
                } else return false;
            } else if (ch == 'e' || ch == 'E') {
                if (seenDigit && !seenExponent) {
                    state = EXPONENT;
                    seenExponent = true;
                    seenDigit = false; // need a digit after exponent
                } else
                    return false;
            } else if (ch == ' ') {
                if (state == INTEGER || state == DECIMAL || state == EXPONENT)
                    state = END;
                else if (state != END)
                    return false;
            } else {
                return false;
            }
        }
        return state == INTEGER || state == DECIMAL || state == END;
    }
};
const isNumber = function(s) {
    const states = [
        {'blank': 0, 'sign': 1, 'digit': 2, '.': 3},
        {'digit': 2, '.': 3},
        {'digit': 2, '.': 4, 'e': 5, 'blank': 8},
        {'digit': 4},
        {'digit': 4, 'e': 5, 'blank': 8},
        {'sign': 6, 'digit': 7},
        {'digit': 7},
        {'digit': 7, 'blank': 8},
        {'blank': 8}
    ];
    let currentState = 0;
    for (const char of s) {
        let transition = '';
        if (char >= '0' && char <= '9') transition = 'digit';
        else if (char === '+' || char === '-') transition = 'sign';
        else if (char === 'e' || char === 'E') transition = 'e';
        else if (char === '.') transition = '.';
        else if (char === ' ') transition = 'blank';
        else transition = '?';
        if (!(transition in states[currentState])) return false;
        currentState = states[currentState][transition];
    }
    return [2, 4, 7, 8].includes(currentState);
};
class Solution {
    public boolean isNumber(String s) {
        int[][] state = {
            { 0, 1, 2, 3, -1, -1}, // state 0
            { -1, -1, 2, 3, -1, -1}, // state 1
            { 8, -1, 2, 4, 5, -1}, // state 2
            { -1, -1, 4, -1, -1, -1}, // state 3
            { 8, -1, 4, -1, 5, -1}, // state 4
            { -1, 6, 7, -1, -1, -1}, // state 5
            { -1, -1, 7, -1, -1, -1}, // state 6
            { 8, -1, 7, -1, -1, -1}, // state 7
            { 8, -1, -1, -1, -1, -1} // state 8
        };
        int currentState = 0;
        for (char c : s.toCharArray()) {
            int transition = -1;
            if (Character.isDigit(c)) transition = 2;
            else if (c == '+' || c == '-') transition = 1;
            else if (c == '.') transition = 3;
            else if (c == 'e' || c == 'E') transition = 4;
            else if (c == ' ') transition = 0;
            if (transition == -1 || state[currentState][transition] == -1) return false;
            currentState = state[currentState][transition];
        }
        return currentState == 2 || currentState == 4 || currentState == 7 || currentState == 8;
    }
}
复杂度分析
时间复杂度是 ,其中 是字符串 的长度。 空间复杂度是 ,因为只需要有限个状态变量来跟踪状态机的状态变化。