zoukankan      html  css  js  c++  java
  • LeetCode(65):有效数字

    Hard!

    题目描述:

    验证给定的字符串是否为数字。

    例如:
    "0" => true
    " 0.1 " => true
    "abc" => false
    "1 a" => false
    "2e10" => true

    说明: 我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。

    解题思路:

    这道验证数字的题比想象中的要复杂的多,有很多情况需要考虑。网上有很多解法,有利用有限自动机Finite Automata Machine的程序,写得简洁优雅(https://blog.csdn.net/kenden23/article/details/18696083); 还有利用正则表达式,更是写的丧心病狂的简洁(https://blog.csdn.net/fightforyourdream/article/details/12900751);这里主要还是用一般的写法,参考了另一篇博文(http://yucoding.blogspot.com/2013/05/leetcode-question-118-valid-number.html)处理各种情况。

    首先,从题目中给的一些例子可以分析出来,我们所需要关注的除了数字以外的特殊字符有空格 ‘ ’, 小数点 '.', 自然数 'e/E', 还要加上正负号 '+/-", 除了这些字符需要考虑意外,出现了任何其他的字符,可以马上判定不是数字。下面我们来一一分析这些出现了也可能是数字的特殊字符:

    1. 空格 ‘ ’ 

    空格分为两种情况需要考虑,一种是出现在开头和末尾的空格,一种是出现在中间的字符。出现在开头和末尾的空格不影响数字,而一旦中间出现了空格,则立马不是数字。

    解决方法:预处理时去掉字符的首位空格,中间再检测到空格,则判定不是数字。

    2. 小数点 '.'

    小数点需要分的情况较多,首先的是小数点只能出现一次,但是小数点可以出现在任何位置,开头(".3"), 中间("1.e2"), 以及结尾("1." ), 而且需要注意的是,小数点不能出现在自然数 'e/E' 之后,如 "1e.1" false, "1e1.1" false。还有,当小数点位于末尾时,前面必须是数字,如 "1."  true," -." false。

    解决方法:开头中间结尾三个位置分情况讨论。

    3. 自然数 'e/E'

    自然数的前后必须有数字,即自然数不能出现在开头和结尾,如 "e" false,  ".e1" false, "3.e" false, "3.e1" true。而且小数点只能出现在自然数之前,还有就是自然数前面不能是符号,如 "+e1" false, "1+e" false。

    解决方法:开头中间结尾三个位置分情况讨论。

    4. 正负号 '+/-"

    正负号可以在开头出现,可以在自然数e之后出现,但不能是最后一个字符,后面得有数字,如  "+1.e+5" true。

    解决方法:开头中间结尾三个位置分情况讨论。

    下面我们开始正式以开头、中间、结尾三个不同位置来分情况进行讨论:

    1. 在讨论开头、中间、结尾三个不同位置之前做预处理,去掉字符串首尾的空格,可以采用两个指针分别指向开头和结尾,遇到空格则跳过,分别指向开头结尾非空格的字符。

    2. 对首字符进行处理,首字符只能为数字或者正负号 '+/-",我们需要定义三个flag以标志我们之前检是否测到过小数点,自然数和正负号。首字符如为数字或正负号,则标记对应的flag,若不是,直接返回false。

    3. 对中间字符的处理,中间字符会出现五种情况,数字,小数点,自然数,正负号和其他字符。

    若是数字,标记flag并通过。

    若是自然数,则必须是第一次出现自然数,并且前一个字符不能是正负号,而且之前一定要出现过数字,才能标记flag通过。

    若是正负号,则之前的字符必须是自然数e,才能标记flag通过。

    若是小数点,则必须是第一次出现小数点并且自然数没有出现过,才能标记flag通过。

    若是其他,返回false。

    4. 对尾字符处理,最后一个字符只能是数字或小数点,其他字符都返回false。

    若是数字,返回true。

    若是小数点,则必须是第一次出现小数点并且自然数没有出现过,还有前面必须是数字,才能返回true。

    C++解法一:

     1 class Solution {
     2 public:
     3     bool isNumber(string s) {
     4         int len = s.size();
     5         int left = 0, right = len - 1;
     6         bool eExisted = false;
     7         bool dotExisted = false;
     8         bool digitExisited = false;
     9         // Delete spaces in the front and end of string
    10         while (s[left] == ' ') ++left;
    11         while (s[right] == ' ') --right;
    12         // If only have one char and not digit, return false
    13         if (left >= right && (s[left] < '0' || s[left] > '9')) return false;
    14         //Process the first char
    15         if (s[left] == '.') dotExisted = true;
    16         else if (s[left] >= '0' && s[left] <= '9') digitExisited = true;
    17         else if (s[left] != '+' && s[left] != '-') return false;
    18         // Process the middle chars
    19         for (int i = left + 1; i <= right - 1; ++i) {
    20             if (s[i] >= '0' && s[i] <= '9') digitExisited = true;
    21             else if (s[i] == 'e' || s[i] == 'E') { // e/E cannot follow +/-, must follow a digit
    22                 if (!eExisted && s[i - 1] != '+' && s[i - 1] != '-' && digitExisited) eExisted = true;
    23                 else return false;
    24             } else if (s[i] == '+' || s[i] == '-') { // +/- can only follow e/E
    25                 if (s[i - 1] != 'e' && s[i - 1] != 'E') return false;                
    26             } else if (s[i] == '.') { // dot can only occur once and cannot occur after e/E
    27                 if (!dotExisted && !eExisted) dotExisted = true;
    28                 else return false;
    29             } else return false;
    30         }
    31         // Process the last char, it can only be digit or dot, when is dot, there should be no dot and e/E before and must follow a digit
    32         if (s[right] >= '0' && s[right] <= '9') return true;
    33         else if (s[right] == '.' && !dotExisted && !eExisted && digitExisited) return true;
    34         else return false;
    35     }
    36 };

    上面的写法略为复杂,我们尝试着来优化一下,根据上面的分析,所有的字符可以分为六大类,空格,符号,数字,小数点,自然底数和其他字符,我们需要五个标志变量,num, dot, exp, sign分别表示数字,小数点,自然底数和符号是否出现,numAfterE表示自然底数后面是否有数字,那么我们分别来看各种情况:

    - 空格: 我们需要排除的情况是,当前位置是空格而后面一位不为空格,但是之前有数字,小数点,自然底数或者符号出现时返回false。

    - 符号:符号前面如果有字符的话必须是空格或者是自然底数,标记sign为true。

    - 数字:标记num和numAfterE为true。

    - 小数点:如果之前出现过小数点或者自然底数,返回false,否则标记dot为true。

    - 自然底数:如果之前出现过自然底数或者之前从未出现过数字,返回false,否则标记exp为true,numAfterE为false。

    - 其他字符:返回false。

    最后返回num && numAfterE即可。

    C++解法二:

     1 class Solution {
     2 public:
     3     bool isNumber(string s) {
     4         bool num = false, numAfterE = true, dot = false, exp = false, sign = false;
     5         int n = s.size();
     6         for (int i = 0; i < n; ++i) {
     7             if (s[i] == ' ') {
     8                 if (i < n - 1 && s[i + 1] != ' ' && (num || dot || exp || sign)) return false;
     9             } else if (s[i] == '+' || s[i] == '-') {
    10                 if (i > 0 && s[i - 1] != 'e' && s[i - 1] != ' ') return false;
    11                 sign = true;
    12             } else if (s[i] >= '0' && s[i] <= '9') {
    13                 num = true;
    14                 numAfterE = true;
    15             } else if (s[i] == '.') {
    16                 if (dot || exp) return false;
    17                 dot = true;
    18             } else if (s[i] == 'e') {
    19                 if (exp || !num) return false;
    20                 exp = true;
    21                 numAfterE = false;
    22             } else return false;
    23         }
    24         return num && numAfterE;
    25     }
    26 };

    这道题给了例子不够用,下面这些例子是在调试的过程中出现过的例子,用来参考:

    string s1 = "0"; // True
    string s2 = " 0.1 "; // True
    string s3 = "abc"; // False
    string s4 = "1 a"; // False
    string s5 = "2e10"; // True
    
    string s6 = "-e10"; // False
    string s7 = " 2e-9 "; // True
    string s8 = "+e1"; // False
    string s9 = "1+e"; // False
    string s10 = " "; // False
    
    string s11 = "e9"; // False
    string s12 = "4e+"; // False
    string s13 = " -."; // False
    string s14 = "+.8"; // True
    string s15 = " 005047e+6"; // True
    
    string s16 = ".e1"; // False
    string s17 = "3.e"; // False
    string s18 = "3.e1"; // True
    string s19 = "+1.e+5"; // True
    string s20 = " -54.53061"; // True
    
    string s21 = ". 1"; // False

    这道题实在是太烦了,情况太多了,简直是无法无天呀~

  • 相关阅读:
    关于ORALE将多行数据合并成为一行 报错未找到where关键字
    Input限制输入数字
    Dev Gridcontrol每行添加序号或者文本。
    Android studio SDK配置
    介数中心性快速计算
    Buuoj 被嗅探的流量
    Docker安装(win10)
    filter CTF
    MySQLdb._exceptions.OperationalError: (2026, 'SSL connection error: unknown error number')
    DNS解析原理(www.baidu.com)
  • 原文地址:https://www.cnblogs.com/ariel-dreamland/p/9151000.html
Copyright © 2011-2022 走看看