(python版)《剑指Offer》JZ53:表示数值的字符串

牛客
Leetcode
在这里插入图片描述

【解题思路】

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

字符类型:

空格 「 」、数字「 0—9 」 、正负号 「 + - 」 、小数点 「 … 」 、幂符号 「 eE 」 。

状态定义:

首先我们得知道什么样的字符串是数值
假设字符串为A.BeC或A.BEC, 也就是整数部分为A小数部分为B指数部分为C,按顺序判断是否包含这三部分。
按照字符串从左到右的顺序,定义以下 9 种状态。

0. 开始的空格
1. 幂符号前的正负号
2. 小数点前的数字
3. 小数点、小数点后的数字
4. 当小数点前为空格时,小数点、小数点后的数字(意思是没有正数部分,直接以小数点开始)
5. 幂符号(过渡状态)
6. 幂符号后的正负号(过渡状态)
7. 幂符号后的数字
8. 结尾的空格
.
其中,合法的结束状态有 2, 3, 7, 8
【状态2的示例】

  • 13(即13.0 小数点后面 可以没有数字),
  • +5或-6(从0状态到2状态 无正负号,从1到2状态 可以有+/-)
  • 错误示例 +-7,一个数字前只能有一个+/-,可以看到 在states[p]=states[2]中找sign,是不存在的,也可以视为越界

【状态3的示例】.14(即0.14 小数点前面 可以没有数字)
【状态7的示例】

  • e5,幂符号后只能有纯数字,不能有小数点/±
  • 错误示例 e5.4或e+5.4

乍一看,有点懵,先看 状态转移图 捋一捋
在这里插入图片描述
定义状态的代码如下:

states = [
	{ ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
    { 'd': 2, '.': 4 } ,                # 1. 'sign' before 'e'
    { 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
    { 'd': 3, 'e': 5, ' ': 8 },         # 3. 'digit' after 'dot'
    { 'd': 3 },                         # 4. 'digit' after 'dot' (‘blank’ before 'dot')
    { 's': 6, 'd': 7 },                 # 5. 'e'
    { 'd': 7 },                         # 6. 'sign' after 'e'
    { 'd': 7, ' ': 8 },                 # 7. 'digit' after 'e'
    { ' ': 8 }                          # 8. end with 'blank'
]
  • 什么意思?
    ==> 这是一个状态列表,每个元素是字典,key值是 判断当前字符所属类型,value值 是下一个跳转的状态编号

    例如:从空格状态0,获得一个数字,属于数字 ‘d’,跳转到状态2
    例如:从状态2,获得一个小数点,属于小数点 ‘.’,跳转到状态3

    有点类似二维数组 states[p][t] 这样,描述一个新状态,作为新的p

  • 那么怎么判断 当前字符所属类型?

    if '0' <= c <= '9': # 一组if结构即可判断
        t = 'd'     # digit
    elif c in "+-": 
            t = 's' # sign
    elif c in "eE": 
        t = 'e'     # e or E
    elif c in ". ": 
        t = c       # dot, blank
    else: 
        t = '?'     # unknown
    

在这里插入图片描述

  • 复杂度分析:
    • 时间复杂度 O(N) : 其中 N 为字符串 s 的长度,判断需遍历字符串,每轮状态转移的使用 O(1) 时间。
    • 空间复杂度 O(1): states 和 p 使用常数大小的额外空间。

假设输入字符串为‘5.2e-3’,则判断过程如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
第四个字符e,是幂符号e,跳到状态5
第五个字符-,是正负号sign,跳到状态6
第六个字符3,是digit,跳到状态7
遍历完字符串,跳出循环,最后一个状态是7,属于合法状态,返回True

完整代码如下

class Solution:
    def isNumber(self, s: str) -> bool:
        states = [
            { ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
            { 'd': 2, '.': 4 } ,                # 1. 'sign' before 'e'
            { 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
            { 'd': 3, 'e': 5, ' ': 8 },         # 3. 'digit' after 'dot'
            { 'd': 3 },                         # 4. 'digit' after 'dot' (‘blank’ before 'dot')
            { 's': 6, 'd': 7 },                 # 5. 'e'
            { 'd': 7 },                         # 6. 'sign' after 'e'
            { 'd': 7, ' ': 8 },                 # 7. 'digit' after 'e'
            { ' ': 8 }                          # 8. end with 'blank'
        ]
        p = 0                           # start with state 0
        for c in s:	# 遍历字符串
            if '0' <= c <= '9': t = 'd' # digit
            elif c in "+-": t = 's'     # sign
            elif c in "eE": t = 'e'     # e or E
            elif c in ". ": t = c       # dot, blank
            else: t = '?'               # unknown
            if t not in states[p]: return False
            p = states[p][t]
        return p in (2, 3, 7, 8)
'''
太赞了!
作者:jyd
链接:https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/solution/mian-shi-ti-20-biao-shi-shu-zhi-de-zi-fu-chuan-y-2/
'''
状态states 长度为: 9
字符串s 的总长度为: 6
当前字符c=5     属于 数字digit
当前状态p = 0   记录字符t = d   记为states[0][d] = 2
故得到新状态p = 2       跳转到states[2]的字典中 找下一个状态

当前字符c = .   属于 小数点.dot
当前状态p = 2   记录字符t = .   记为states[2][.] = 3
故得到新状态p = 3       跳转到states[3]的字典中 找下一个状态

当前字符c=2     属于 数字digit
当前状态p = 3   记录字符t = d   记为states[3][d] = 3
故得到新状态p = 3       跳转到states[3]的字典中 找下一个状态

当前字符c = e   属于 幂符号e
当前状态p = 3   记录字符t = e   记为states[3][e] = 5
故得到新状态p = 5       跳转到states[5]的字典中 找下一个状态

当前字符c = -   属于 正负号sign
当前状态p = 5   记录字符t = s   记为states[5][s] = 6
故得到新状态p = 6       跳转到states[6]的字典中 找下一个状态

当前字符c=3     属于 数字digit
当前状态p = 6   记录字符t = d   记为states[6][d] = 7
故得到新状态p = 7       跳转到states[7]的字典中 找下一个状态

True

【打印版】

def isNumber(s):
    states = [
        { ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
        { 'd': 2, '.': 4 } ,                # 1. 'sign' before 'e'
        { 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
        { 'd': 3, 'e': 5, ' ': 8 },         # 3. 'digit' after 'dot'
        { 'd': 3 },                         # 4. 'digit' after 'dot' (‘blank’ before 'dot')
        { 's': 6, 'd': 7 },                 # 5. 'e'
        { 'd': 7 },                         # 6. 'sign' after 'e'
        { 'd': 7, ' ': 8 },                 # 7. 'digit' after 'e'
        { ' ': 8 }                          # 8. end with 'blank'
    ]
    print('状态states 长度为:',len(states))
    p = 0                           # start with state 0
    print('字符串s 的总长度为:',len(s))
    for c in s:				# 遍历字符串
        if '0' <= c <= '9': # 判断 当前字符所属类型
            t = 'd' # digit
            print('当前字符c=%s\t属于 数字digit'%c)
        elif c in "+-": 
            t = 's'     # sign
            print('当前字符c = %s\t属于 正负号sign'%c)
        elif c in "eE": 
            t = 'e'     # e or E
            print('当前字符c = %s\t属于 幂符号e'%c)
        elif c in ". ": 
            t = c       # dot, blank
            print('当前字符c = %s\t属于 小数点.dot'%c)
        else: 
            t = '?'               # unknown
            print('当前字符c = %s\tUnknown'%c)
        if t not in states[p]: 
            return False
        print('当前状态p =',p,end='\t')
        print('记录字符t =',t,end='\t')
        print('记为states[%s][%s] = %d'%(p,t,states[p][t]))
        p = states[p][t]
        print('故得到新状态p =',p,end='\t')
        print('跳转到states[%s]的字典中 找下一个状态\n'%p)
    return p in (2, 3, 7, 8)

if __name__ == "__main__":
    s = '5.2e-3'
    print(isNumber(s))

关于Leetcode用户@白露塞纳的几个疑问

  1. 为什么状态3和4中的小数点不独立出来作为一个状态呢?
  2. 状态4->3不太理解,为什么这样转?
  3. 状态1->4 【如“ + .1" , return false】 为什么可以?
  4. 状态1->3 【如 “+.1” , return true】
    4->5 【如 “+.1e2” , return true】
    4->8 【如 ".1 " , return true】
    为什么不可以?

.
我的解答是如下:

  1. 状态3和4其实是一样的,区别是状态3是间接到达,是过渡阶段;状态4是直接到达。
    独立一个状态其实也是可以的,看这张草图
    在这里插入图片描述
  2. 状态4到3,等同与状态3到3,都是小数点后 获得数字的 状态。
  3. 字符串‘+.1’ 的状态转化为:0-1-4-3,按照图上画的,也都是可以转化的,return True,是表示数值
  4. 字符串’+.1e2’ 的状态转化为:0-1-4-3-5-7,正确转化,return True,是表示数值
    字符串‘.1’ 的转化状态为:0-4-3,正确转化,return True,是表示数值

.
我也有个疑问是:为什么8是合法的结束状态呢?为什么不是到7就停止了?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值