天天看點

力扣每日一題:65. 有效數字題目:65. 有效數字解題思路

目錄

  • 題目:65. 有效數字
    • 示例1
    • 示例2
    • 示例3
    • 示例4
    • 提示
  • 解題思路
    • (1)模拟法
    • (2)狀态機
    • (3)正規表達式

題目:65. 有效數字

難度: 困難

題目:

有效數字(按順序)可以分成以下幾個部分:

  • 一個 小數 或者 整數
  • (可選)一個 ‘e’ 或 ‘E’ ,後面跟着一個 整數

小數(按順序)可以分成以下幾個部分:

  • (可選)一個符号字元(’+’ 或 ‘-’)
  • 下述格式之一:
    • 至少一位數字,後面跟着一個點 ‘.’
    • 至少一位數字,後面跟着一個點 ‘.’ ,後面再跟着至少一位數字
    • 一個點 ‘.’ ,後面跟着至少一位數字

整數(按順序)可以分成以下幾個部分:

  • (可選)一個符号字元(’+’ 或 ‘-’)
  • 至少一位數字

部分有效數字列舉如下:

  • [“2”, “0089”, “-0.1”, “+3.14”, “4.”, “-.9”, “2e10”, “-90E3”, “3e+7”, “+6e-1”, “53.5e93”, “-123.456e789”]

部分無效數字列舉如下:

  • [“abc”, “1a”, “1e”, “e3”, “99e2.5”, “–6”, “-+3”, “95a54e53”]

給你一個字元串 s ,如果 s 是一個 有效數字 ,請傳回 true 。

示例1

輸入:s = “0”

輸出:true

示例2

輸入:s = “e”

輸出:false

示例3

輸入:s = “.”

輸出:false

示例4

輸入:s = “.1”

輸出:true

提示

  • 1 <= s.length <= 20

  • s

    僅含英文字母(大寫和小寫),數字(0-9),加号 ‘+’ ,減号 ‘-’ ,或者點 ‘.’ 。

來源:力扣(LeetCode)

連結:https://leetcode-cn.com/problems/valid-number/

著作權歸領扣網絡所有。商業轉載請聯系官方授權,非商業轉載請注明出處。

解題思路

(1)模拟法

最容易想到的方法就是模拟法,根據「有效數字定義」梳理各部分的限制。

定義幾個标志辨別:

isE

:是否有’E’或’e’,

isDot

:是否有點,

isSign

:是否有符号,

isNum

:是否有數字。

周遊字元串

s

,根據設計規則有以下情況:

  • 遇到’E’或’e’:(1)已經有E了,錯誤(2)E之前需要有數字
  • 遇到’+‘或’-’:(1)已有符号,錯誤(2)符号之前須有數字(3)符号之前不可有點
  • 遇到’.’://(1)已有點,錯誤(2)科學計算法之後需要是整數,是以不可有點(小數)
  • 遇到數字:

    isNum = true

  • 其它情況,

    return false

考慮到科學記數法的特殊性,是以在遇到’E’和’e’時,需要将

isE

isDot

isSign

三個标志位清除,因為在之後的判斷中又是重新的一輪判斷,不可被之前所影響。

class Solution {
public:
    bool isNumber(string s) {

        int n = s.size();
        int i = 0;
        bool isE = false, isDot = false, isSign = false, isNum = false;

        while (i < n)
        {
            if (s[i] == 'e' || s[i] == 'E')
            {
                //(1)已經有E了,錯誤(2)E之前需要有數字
                if (isE || !isNum) return false;
                //有了E之後,點、符号、數字等辨別符清空,因為之後又是一個新的是否有效數字判斷,該步十分關鍵
                isE = true; isDot = false; isSign = false; isNum = false;
            }
            else if (s[i] == '+' || s[i] == '-')
            {
                //(1)已有符号,錯誤(2)符号之前須有數字(3)符号之前不可有點
                if (isSign || isNum || isDot) return false;
                isSign = true;
            }
            else if (s[i] == '.')
            {
                //(1)已有點,錯誤(2)科學計算法之後需要是整數,是以不可有點(小數)
                if (isDot || isE) return false;
                isDot = true;
            }
            else if (s[i] >= '0' && s[i] <= '9')
            {
                isNum = true;
            }
            else
            {
                return false;
            }

            i++;
        }
        return isNum;
    }
};
           

(2)狀态機

确定有限狀态自動機,有一系列狀态:「初始狀态」、「接受狀态」。在算法領域,它與大名鼎鼎的字元串查找算法KMP 算法有着密切的關聯;在工程領域,它是實作正規表達式的基礎。

其運作的流程如下:

  • 起初,這個自動機處于「初始狀态」。
  • 随後,它順序地讀取字元串中的每一個字元,并根據目前狀态和讀入的字元,按照某個事先約定好的「轉移規則」,從目前狀态轉移到下一個狀态;
  • 當狀态轉移完成後,它就讀取下一個字元。
  • 當字元串全部讀取完畢後,如果自動機處于某個「接受狀态」,則判定該字元串「被接受」;否則,判定該字元串「被拒絕」。

對于本題,因為不同的字元将導緻判斷進入不同的狀态,是以使用狀态機求解是個不錯的選擇。用「目前處理到字元串的哪個部分」當作狀态的表述。根據這一技巧,不難挖掘出所有狀态:

  1. 初始狀态
  2. 符号位
  3. 整數部分
  4. 左側有整數的小數點
  5. 左側無整數的小數點(根據前面的第二條額外規則,需要對左側有無整數的兩種小數點做區分)
  6. 小數部分
  7. 字元 e
  8. 指數部分的符号位
  9. 指數部分的整數部分

根據合法字元串的格式,狀态機的轉換如下:

力扣每日一題:65. 有效數字題目:65. 有效數字解題思路

同時,轉移失敗即下一字元找不到「接受狀态」,則判斷該字元串「不接受」,比對結束。

class Solution {
public:
    enum State 
    {
        STATE_INITIAL,
        STATE_SIGN,
        STATE_INTEGER,
        STATE_DOT_DECIMALS,
        STATE_DOT_INTEGER,
        STATE_DOT,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUM,
        STATE_END
    };

    enum CharType
    {
        CHAR_NUM,
        CHAR_EXP,
        CHAR_DOT,
        CHAR_SIGN,
        CHAR_ERROR
    };
    CharType getType(char ch)
    {
        if (ch >= '0' && ch <= '9')
        {
            return CHAR_NUM;
        }
        else if (ch == 'E' || ch == 'e')
        {
            return CHAR_EXP;
        }
        else if (ch == '.')
        {
            return CHAR_DOT;
        }
        else if (ch == '+' || ch == '-')
        {
            return CHAR_SIGN;
        }
        else
        {
            return CHAR_ERROR;
        }
    }
    bool isNumber(string s) {

        unordered_map<State, unordered_map<CharType, State>> mapState= {

            {
                STATE_INITIAL, {
                    {CHAR_NUM, STATE_INTEGER},
                    {CHAR_DOT, STATE_DOT_DECIMALS},
                    {CHAR_SIGN, STATE_SIGN}
                }
            },

            {
                STATE_SIGN, {
                    {CHAR_NUM, STATE_INTEGER},
                    {CHAR_DOT, STATE_DOT_DECIMALS}
                }
            },

            {
                STATE_INTEGER, {
                    {CHAR_NUM, STATE_INTEGER},
                    {CHAR_DOT, STATE_DOT_INTEGER},
                    {CHAR_EXP, STATE_EXP}
                }
            },

            {
                STATE_DOT_DECIMALS, {
                    {CHAR_NUM, STATE_DOT}
                }
            },

            {
                STATE_DOT_INTEGER, {
                    {CHAR_NUM, STATE_DOT},
                    {CHAR_EXP, STATE_EXP}
                }
            },

            {
                STATE_DOT, {
                    {CHAR_NUM, STATE_DOT},
                    {CHAR_EXP, STATE_EXP}
                }
            },

            {
                STATE_EXP, {
                    {CHAR_SIGN, STATE_EXP_SIGN},
                    {CHAR_NUM, STATE_EXP_NUM}
                }
            },

            {
                STATE_EXP_SIGN, {
                    {CHAR_NUM, STATE_EXP_NUM}
                }
            },

            {
                STATE_EXP_NUM, {
                    {CHAR_NUM, STATE_EXP_NUM}
                }
            }
        };


        //開始
        int n = s.size();
        State st = STATE_INITIAL;

        for (int i = 0; i < n; i++)
        {
            CharType ct = getType(s[i]);
            if (mapState[st].find(ct) == mapState[st].end())
            {
                return false;
            }
            else
            {
                st = mapState[st][ct];//修改狀态
            }
        }

        //整數、小數、科學記數法整數、左邊為整數小數點
        return st == STATE_INTEGER || st == STATE_DOT || st == STATE_EXP_NUM || st == STATE_DOT_INTEGER;
        
    }
};
           

(3)正規表達式

注意,正規表達式pattern使用類的靜态變量或者全局變量,避免重複構造的開銷。

class Solution {
public:
    static const regex pattern;

    bool isNumber(string s) {

        return regex_match(s, pattern);
    }
};
const regex Solution::pattern("[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[Ee][+-]?\\d+)?");

           
const regex pattern = regex("[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[Ee][+-]?\\d+)?");
class Solution {
public:
    bool isNumber(string s) {

        return regex_match(s, pattern);
    }
};