LeetCode 表示数值的字符串

剑指 Offer 20. 表示数值的字符串

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

数值(按顺序)可以分成以下几个部分:

  1. 若干空格

  2. 一个 小数 或者 整数

  3. (可选)一个 'e' 或 'E' ,后面跟着一个 整数

  4. 若干空格

小数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-')

  2. 下述格式之一:

  3. 至少一位数字,后面跟着一个点 '.'

  4. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字

  5. 一个点 '.' ,后面跟着至少一位数字

整数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-')

  2. 至少一位数字

部分数值列举如下:

  • ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]

部分非数值列举如下:

  • ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]

示例 1:

输入:s = "0"
输出:true

示例 2:

输入:s = "e"
输出:false

示例 3:

输入:s = "."
输出:false

示例 4:

输入:s = "    .1  "
输出:true

提示:

  • 1 <= s.length <= 20

  • s 仅含英文字母(大写和小写),数字(0-9),加号 '+' ,减号 '-' ,空格 ' ' 或者点 '.' 。

作者:Krahets 链接:力扣 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路

本题使用有限状态自动机。根据字符类型和合法数值的特点,先定义状态,最后编写代码即可。

代码用一个 while 循环扫描整个字符串,只处理一次每个字符,并且避免了很多复杂的判断。

  • 在跳过前导空格和正负号后,每次处理当前字符时,根据不同情况更新若干 bool 变量,表示是否已经看到数字、小数点、科学计数法符号以及它们之间的各种关系。

  • 如果当前字符不是数字、小数点、科学计数法符号、正负号或空格,则说明字符串不合法,直接返回 false。

  • 处理完字符串后,再跳过末尾空格,检查数字和科学计数法符号出现的正确性以及结尾是否为数字即可。

字符类型:

空格 「 」、数字「 0—9 」 、正负号 「 +, − 」 、小数点 「 . 」 、幂符号 「 e, E 」

状态定义

  • seen_num = false 数字是否存在

  • seen_dot = false 小数点是否存在

  • seen_e = false 指数e/E是否存在

  • seen_digit_after_e = true 指数e/E后面的数字是否存在

最后根据各种可能情况返回 true 或 false

第一步:

先跳过字符串开头的空格

int i = 0; 
while (i < len && s[i] == ' ') i++; 

 第二步:

如果遇到+、-号则跳过

if (i < len && (s[i] == '+' || s[i] == '-')) i++; 

 第四步:

​​​​​​​开始遍历字符串

如果是数字,则将数字状态seen_num变为true

                     并且指数后面的数字状态seen_digit_after_e也变为true

如果是小数点,则将小数点状态seen_num变为true

如果之前出现过小数点或者指数,则不合法,需要return false

例如:

3.14.15(不合法,出现>1次小数点)

-3.14e+1.5(不合法,指数e后面出现>0次小数点)

 

//如果是数字
if (c>='0'&&c<='9') { 
    seen_num = true;
    seen_digit_after_e = true; 
}
// 如果是小数点
else if (c == '.') { 
    if (seen_dot || seen_e) return false; 
    seen_dot = true; 
} 

第五步:

当遇到指数e/E的时候,有两种不合法情况:

  1. 指数前面没有出现数字        例如:e+5、+e5+5
  2. 指数前面已经出现过指数     例如:-3.14e+5e+5

此时则需要return false

假如没有出现不合况,则将seen_e变为true

指数后面的数字状态seen_digit_after_e重置为false(用来监测指数后面是否出现没有数字(不合法)情况    例如:-3.14e)

// 如果是指数符号
else if (c == 'e' || c == 'E') { 
    if (!seen_num || seen_e) return false; 
        seen_e = true; 
        seen_digit_after_e = false; 
} 

第六步:

如果在遍历字符串的过程中又遇到了+、-号(因为在遍历之前已经跳过一次+、-),此次需要检查+、-号是否出现在指数后面,否则会出现以下不合法情况:

  1. 在指数前出现两次+、-号        例如:-3.14+e+5
  2. 在除了指数后面一位的位置出现了+、-号        例如:-3.14e5+、-3.14e+5-5

合法情况为在跳过了开头+、-号的情况下,只能在指数e/E后面一位出现+、-号,如果合法则跳过。

 

// 如果是正负号
else if (c == '+' || c == '-') { 
    if (s[i-1] != 'e' && s[i-1] != 'E') return false; 
} 

第七步:

一直遍历下去,如果遇到其他字符,则为非法,return false

直到再次遇到空格则跳出遍历,开始进行末尾空格状态检查

// 如果是空格
else if (c == ' ') { 
    break; // 跳出循环
} 
// 其他情况都是非法字符
else { 
    return false; // 返回 false
}       
i++; // 指针向右移动一位

 第八步:

检查结尾空格,如果检查过程中又遇到了非空格字符,则视为不合法,return false

例如:-3.14e+5   67 此处5后面开始检查空格,检查过程中遇到了6则停止检查,不合法 

 while (i < len && s[i] == ' ') i++; 

 第九步:

最后判断是否满足数值形式:

  1. seen_num==true        字符串中必须有数字
  2. seen_digit_after_e==true     指数后面必须有数字
  3. i == len;        成功遍历到末尾(因为空格检查的时候使用了i++,所以此处不是i==len-1)​​​​​​​

全部判断成功后则返回true,否则返回false

若顺利检查到字符串末尾,未出现不合法情况则结束检查

return seen_num && seen_digit_after_e && i == len; 

​​​​​​​

 

本人通过代码(附注释)

#include<iostream>
#include<string>
using namespace std;
class Solution {
public:
   bool isNumber(string s) {
   		// 获取字符串长度
        int len = s.length(); 
        // 初始化标志位
        bool seen_num = false, seen_dot = false, seen_e = false, seen_digit_after_e = true; 
        // 初始化指针
        int i = 0; 
        // 跳过字符串开头的空格
        while (i < len && s[i] == ' ') i++; 
        // 如果是正负号,跳过
        if (i < len && (s[i] == '+' || s[i] == '-')) i++; 
        // 遍历字符串
        while (i < len) { 
            char c = s[i]; // 获取当前字符
            // 如果是数字
            if (c>='0'&&c<='9') { 
                seen_num = true; // 设置数字标志位为 true
                seen_digit_after_e = true; // 若之前出现过 e,将指数后面的数字标志位也设置为 true
            }
            // 如果是小数点
			else if (c == '.') { 
                if (seen_dot || seen_e) return false; // 如果已经出现过小数点或者指数,返回 false
                seen_dot = true; // 设置小数点标志位为 true
            } 
			// 如果是指数符号
			else if (c == 'e' || c == 'E') { 
                if (!seen_num || seen_e) return false; // 如果之前没有出现过数字或者已经出现过指数,返回 false
                seen_e = true; // 设置指数标志位为 true
                seen_digit_after_e = false; // 将指数后面的数字标志位设置为 false
            } 
			// 如果是正负号
			else if (c == '+' || c == '-') { 
                if (s[i-1] != 'e' && s[i-1] != 'E') return false; // 如果前一个字符不是 e 或 E,返回 false
            } 
			// 如果是空格
			else if (c == ' ') { 
                break; // 跳出循环
            } 
			// 其他情况都是非法字符
			else { 
                return false; // 返回 false
            }
            
            i++; // 指针向右移动一位
        }
        // 跳过结尾的空格(如果跳过空格的过程中又遇到了不是空格的字符则终止遍历,视为false) 
        while (i < len && s[i] == ' ') i++; 
        // 判断是否满足数值形式(必须存在数字,并且指数e后面也必须有数字,并且能够遍历字符串到结尾
        return seen_num && seen_digit_after_e && i == len; 
    }
};
int main(){
    Solution sol; 
    string s;
    getline(cin,s); // 获取用户输入的字符串(支持字符串中有空格输入)
    if(sol.isNumber(s)) cout<<"true"; // 如果是数值形式,输出 true
    else cout<<"false"; // 否则输出 false
    return 0; 
}

这个代码的时间复杂度为 O(n),空间复杂度为 O(1)。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖c个铁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值