65、有效的数字

  1. 说明:
  • 代码
  • 验证给定的字符串是否可以解释为十进制数字。

    例如:

    "0" => true
    " 0.1 " => true
    "abc" => false
    "1 a" => false
    "2e10" => true
    " -90e3   " => true
    " 1e" => false
    "e3" => false
    " 6e-1" => true
    " 99e2.5 " => false
    "53.5e93" => true
    " --6 " => false
    "-+3" => false
    "95a54e53" => false
    

    说明:

    • 我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。这里给出一份可能存在于有效十进制数字中的字符列表:

    • 数字 0-9

    • 指数 - “e”

    • 正/负号 - “+”/“-“

    • 小数点 - “.”

    • 当然,在输入中,这些字符的上下文也很重要。

    代码

    • 直接来,设计以下错误识别方式
    class Solution {
        public boolean isNumber(String s) {
            s = s.trim();
            boolean pointSeen = false;
            boolean eSeen = false;
            boolean numberSeen = false;
            boolean numberAfterE = true;
    
            for (int i = 0;i < s.length();i++) {
                if (s.charAt(i) <= '9' && s.charAt(i) >= '0') {
                    numberSeen = true;
                    numberAfterE = true;
                } else if (s.charAt(i) == '.'){
                    if(eSeen || pointSeen) {
                        return false;
                    }
                    pointSeen = true;
                } else if (s.charAt(i) == 'e') {
                    if (eSeen || !numberSeen) {
                        return false;
                    }
                    numberAfterE = false;
                    eSeen = true;
                } else if (s.charAt(i) == '-' || s.charAt(i) == '+') {
                    if (i != 0 && s.charAt(i-1) != 'e') {
                        return false;
                    }
                } else {
                    return false;
                }
            }
            return numberAfterE && numberSeen;
        }
    }
    
    // 巧妙设计以下错误识别方法,遍历过程中,把遇到不符合的都返回 false,到最后成功到达末尾就返回 true。C++ 的代码
    bool isNumber(const char *s) 
    {
        int i = 0;
    
        // skip the whilespaces
        for(; s[i] == ' '; i++) {}
    
        // check the significand
        if(s[i] == '+' || s[i] == '-') i++; // skip the sign if exist
    
        // nm:数字 pt:小数点
        int n_nm, n_pt;
    
        for(n_nm=0, n_pt=0; (s[i]<='9' && s[i]>='0') || s[i]=='.'; i++)
            s[i] == '.' ? n_pt++:n_nm++;       
        if(n_pt>1 || n_nm<1) // no more than one point, at least one digit
            return false;
    
        // check the exponent if exist
        if(s[i] == 'e') {
            i++;
            if(s[i] == '+' || s[i] == '-') i++; // skip the sign
    
            int n_nm = 0; // 数字 
            for(; s[i]>='0' && s[i]<='9'; i++, n_nm++) {}
            if(n_nm<1)
                return false;
        }
    
        // skip the trailing whitespaces
        for(; s[i] == ' '; i++) {}
    
        return s[i]==0;  // must reach the ending 0 of the string
    }
    
    // 作者:windliang
    // 链接:https://leetcode-cn.com/problems/valid-number/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-1-4/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 确定无限自动机,设计状态跳转表

    • DFA 作为确定的有限状态机,比 NFA 更加实用,因为对于每一个状态接收的下一个字符,DFA 能确定唯一一条转换路径,所以使用简单的表驱动的一些方法就可以实现,并且只需要读一遍输入流,比起 NFA 需要回读在速度上会有所提升。

    • 构建出来的状态机如封面图片所示(红色为 终止状态,蓝色为 中间状态)。根据《编译原理》的解释,DFA 从状态 0 接受串 s 作为输入。当s耗尽的时候如果当前状态处于中间状态,则拒绝;如果到达终止状态,则接受。

    • 然后,根据 DFA 列出如下的状态跳转表,之后我们就可以采用 表驱动法 进行编程实现了。需要注意的是,这里面多了一个状态 8,是用于处理串后面的若干个多余空格的。所以,所有的终止态都要跟上一个状态 8。其中,有一些状态标识为-1,是表示遇到了一些意外的字符,可以直接停止后续的计算。状态跳转表如下:

    tu
    tu

    作者:user8973
    链接:https://leetcode-cn.com/problems/valid-number/solution/biao-qu-dong-fa-by-user8973/

    class Solution {
        public int make(char c) {
            switch(c) {
                case ' ': return 0;
                case '+':
                case '-': return 1;
                case '.': return 3;
                case 'e': return 4;
                default:
                    if(c >= 48 && c <= 57) return 2;
            }
            return -1;
        }
        
        public boolean isNumber(String s) {
            int state = 0;
            int finals = 0b101101000;
            int[][] transfer = new int[][]{{ 0, 1, 6, 2,-1},
                                           {-1,-1, 6, 2,-1},
                                           {-1,-1, 3,-1,-1},
                                           { 8,-1, 3,-1, 4},
                                           {-1, 7, 5,-1,-1},
                                           { 8,-1, 5,-1,-1},
                                           { 8,-1, 6, 3, 4},
                                           {-1,-1, 5,-1,-1},
                                           { 8,-1,-1,-1,-1}};
            char[] ss = s.toCharArray();
            for(int i=0; i < ss.length; ++i) {
                int id = make(ss[i]);
                if (id < 0) return false;
                state = transfer[state][id];
                if (state < 0) return false;
            }
            return (finals & (1 << state)) > 0;
        }
    }
    
    // 作者:user8973
    // 链接:https://leetcode-cn.com/problems/valid-number/solution/biao-qu-dong-fa-by-user8973/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 采用责任链模式,各个部分负责自己的事情
    /每个类都实现这个接口
    interface NumberValidate { 
        boolean validate(String s);
    }
    //定义一个抽象类,用来检查一些基础的操作,是否为空,去掉首尾空格,去掉 +/-
    //doValidate 交给子类自己去实现
    abstract class  NumberValidateTemplate implements NumberValidate{
    
        public boolean validate(String s)
        {
            if (checkStringEmpty(s))
            {
                return false;
            }
    
            s = checkAndProcessHeader(s);
    
            if (s.length() == 0)
            {
                return false;
            }
    
            return doValidate(s);
        }
    
        private boolean checkStringEmpty(String s)
        {
            if (s.equals(""))
            {
                return true;
            }
    
            return false;
        }
    
        private String checkAndProcessHeader(String value)
        {
            value = value.trim();
    
            if (value.startsWith("+") || value.startsWith("-"))
            {
                value = value.substring(1);
            }
    
    
            return value;
        }
    
    
    
        protected abstract boolean doValidate(String s);
    }
    
    //实现 doValidate 判断是否是整数
    class IntegerValidate extends NumberValidateTemplate{
    
        protected boolean doValidate(String integer)
        {
            for (int i = 0; i < integer.length(); i++)
            {
                if(Character.isDigit(integer.charAt(i)) == false)
                {
                    return false;
                }
            }
    
            return true;
        }
    }
     
    //实现 doValidate 判断是否是科学计数法
    class SienceFormatValidate extends NumberValidateTemplate{
    
        protected boolean doValidate(String s)
        {
            s = s.toLowerCase();
            int pos = s.indexOf("e");
            if (pos == -1)
            {
                return false;
            }
    
            if (s.length() == 1)
            {
                return false;
            }
    
            String first = s.substring(0, pos);
            String second = s.substring(pos+1, s.length());
    
            if (validatePartBeforeE(first) == false || validatePartAfterE(second) == false)
            {
                return false;
            }
    
    
            return true;
        }
    
        private boolean validatePartBeforeE(String first)
        {
            if (first.equals("") == true)
            {
                return false;
            }
    
            if (checkHeadAndEndForSpace(first) == false)
            {
                return false;
            }
    
            NumberValidate integerValidate = new IntegerValidate();
            NumberValidate floatValidate = new FloatValidate();
            if (integerValidate.validate(first) == false && floatValidate.validate(first) == false)
            {
                return false;
            }
    
            return true;
        }
    
        private boolean checkHeadAndEndForSpace(String part)
        {
    
            if (part.startsWith(" ") ||
                part.endsWith(" "))
            {
                return false;
            }
    
            return true;
        }
    
        private boolean validatePartAfterE(String second)
        {
            if (second.equals("") == true)
            {
                return false;
            }
    
            if (checkHeadAndEndForSpace(second) == false)
            {
                return false;
            }
    
            NumberValidate integerValidate = new IntegerValidate();
            if (integerValidate.validate(second) == false)
            {
                return false;
            }
    
            return true;
        }
    }
    //实现 doValidate 判断是否是小数
    class FloatValidate extends NumberValidateTemplate{
    
        protected boolean doValidate(String floatVal)
        {
            int pos = floatVal.indexOf(".");
            if (pos == -1)
            {
                return false;
            }
    
            if (floatVal.length() == 1)
            {
                return false;
            }
    
            NumberValidate nv = new IntegerValidate();
            String first = floatVal.substring(0, pos);
            String second = floatVal.substring(pos + 1, floatVal.length());
    
            if (checkFirstPart(first) == true && checkFirstPart(second) == true)
            {
                return true;
            }
    
            return false;
        }
    
        private boolean checkFirstPart(String first)
        {
            if (first.equals("") == false && checkPart(first) == false)
            {
                return false;
            }
    
            return true;
        }
    
        private boolean checkPart(String part)
        {
            if (Character.isDigit(part.charAt(0)) == false ||
                Character.isDigit(part.charAt(part.length() - 1)) == false)
            {
                return false;
            }
    
            NumberValidate nv = new IntegerValidate();
            if (nv.validate(part) == false)
            {
                return false;
            }
    
            return true;
        }
    }
    //定义一个执行者,我们把之前实现的各个类加到一个数组里,然后依次调用
    class NumberValidator implements NumberValidate {
    
        private ArrayList<NumberValidate> validators = new ArrayList<NumberValidate>();
    
        public NumberValidator()
        {
            addValidators();
        }
    
        private  void addValidators()
        {
            NumberValidate nv = new IntegerValidate();
            validators.add(nv);
    
            nv = new FloatValidate();
            validators.add(nv);
    
            nv = new HexValidate();
            validators.add(nv);
    
            nv = new SienceFormatValidate();
            validators.add(nv);
        }
    
        @Override
        public boolean validate(String s)
        {
            for (NumberValidate nv : validators)
            {
                if (nv.validate(s) == true)
                {
                    return true;
                }
            }
    
            return false;
        }
    
    
    }
    public boolean isNumber(String s) {
        NumberValidate nv = new NumberValidator(); 
        return nv.validate(s);
    }
    
    
    // 作者:windliang
    // 链接:https://leetcode-cn.com/problems/valid-number/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-1-4/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

    💰

    Title:65、有效的数字

    Count:1.9k

    Author:攀登

    Created At:2020-07-26, 00:19:44

    Updated At:2024-06-15, 15:52:32

    Url:http://jiafeimao-gjf.github.io/2020/07/26/65%E3%80%81%E6%9C%89%E6%95%88%E7%9A%84%E6%95%B0%E5%AD%97/

    Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

    ×

    Help us with donation