python每日一题【剑指 Offer 20. 表示数值的字符串】

day10-2022.11.04

题目信息来源
作者:Krahets
链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm
来源:力扣(LeetCode)

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

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

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

  1. 若干空格
  2. 一个 小数 或者 整数
  3. (可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个 整数
  4. 若干空格

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

  1. (可选)一个符号字符(‘+’ 或 ‘-’)
  2. 下述格式之一:
    • 至少一位数字,后面跟着一个点 ‘.’
    • 至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字
    • 一个点 ‘.’ ,后面跟着至少一位数字

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

  1. (可选)一个符号字符(‘+’ 或 ‘-’)
  2. 至少一位数字

部分数值列举如下:[“+100”, “5e2”, “-123”, “3.1416”, “-1E-16”, “0123”]

部分非数值列举如下:[“12e”, “1a3.14”, “1.2.3”, “±5”, “12e+5.4”]

题解一:个人向

这应该是目前最恶心的一道题了,如果用if else来做的话。首先可以明确的是,这些字符串应该是分为以下几类 .[0~9] +/-e/E,我这里是用来一个函数判断两个相连的字符之间的连接是否符合规则,但在我写的时却漏掉了很多情况。真的很麻烦。并且对于可能在连接中多次出现的字符,需要一个标记来判断。显然不是最优解。

class Solution:
    def isNumber(self, s: str) -> bool:
        if not s:return
        if len(s)==1:
            if s in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:return True
            else:return False
        else:
            self.dot_flag = False           # 标记是否出现过 . 出现过之后,后面不能再出现.
            self.e_flag = False             # 标记是否出现过 e/E 出现过之后,后面不能再出现 . e/E
            self.flag = False               # 标记正负号,如果出现过,后面不能再出现+/-
            self.num = False                # 是否出现过数字,e/E不能用来开头
            result = None
            # 整理逻辑时,一旦检查出不合规就return,如果正确或者还不能判断,就继续检查
            for i in range(len(s)-1):
                result = self.isNum(s[i], s[i+1])
                if result==False:
                    break
            if result==None:
                result = False
            return result
            
    def isNum(self, s:str, s_next:str) -> bool:
        check_result = None
        # 空格在前
        if s==' ':
            # 首先是中间插入空格的情况
            if s_next==' ':
                check_result = True
            elif self.dot_flag==True or self.e_flag==True or self.e_flag==True or self.num==True:
                check_result = False
            # 空格后接.,首次出现,此时还不能判断正确,因为必须要有一个数字才可以确定,check_result不改变
            elif s_next=='.':self.dot_flag = True
            # 空格后接数字,首次出现,到目前为止格式还是正确的
            elif s_next in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
                check_result = True
                self.num = True
            # 如果后面还是空格,继续检查,还不能判断
            # if s_next==' ':check_result = None
            # 如果后面接e/E,首次出现,肯定为不合格
            elif s_next=='e' or s_next=='E':
                check_result = False
            # 如果后面是+/-,首次出现,还不能判断
            elif s_next=='+' or s_next=='-':
                self.flag = True
        # . 在前
        elif s=='.':
            self.dot_flag = True
            if s_next in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
                check_result = True
                self.num = True
            elif s_next==' ':
                if self.num==True:check_result = True
                else:check_result = False
            elif s_next in ['.', '-', '+']:
                check_result = False
        # e/E在前
        elif s=='e' or s=='E':
            self.e_flag = True
            # 前面没有出现过数字,一定是错的
            if self.num==False:
                check_result = False
            # 后面接数字,合格
            elif s_next in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
                check_result = True
            # 后面接空格 . e/E不合格
            elif s_next in [' ', '.', 'e', 'E']:
                check_result = False
            # 后面接正负号,不确定是否合格
            # elif s_next in ['+', '-']:check_result=None
        # +/-在前
        elif s=='+' or s=='-':
            self.flag = True
            if s_next in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
                check_result = True
                self.num = True
            elif s_next=='.':
                self.dot_flag = True
            elif s_next in [' ', 'e', 'E', '+', '-']:
                check_result = False
        # 数字在前
        elif s in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
            # 如果下一个也是数字,合理
            self.num = True
            if s_next in ['+', '-']:
                check_result = False
            elif s_next in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ']:
                check_result = True
            # 后面接 . e/E不确定,如果第二次出现这两个符号,肯定错
            elif s_next=='.':
                if self.dot_flag==True or self.e_flag==True: check_result = False
                else:
                    self.dot_flag = True
                    check_result = True
            elif s_next in ['e', 'E']:
                if self.e_flag==True: check_result = False
                else:self.e_flag = True
        else:
            check_result = False
        return check_result

# myclass = Solution()
# result = myclass.isNumber(".  ")
# print(result)

题解:有限状态自动机

官方题解,说明了这种题的解决步骤:根据字符类型和合法数值的特点,先定义状态,再画出状态转移图,最后编写代码即可。步骤就是

  1. 确定字符类型:字符类型比较好确定,.[0~9] +/-e/E,共五种
  2. 明确什么是合法数值:合法数值,题干里说了,所以可以试着大概说一下
    1. 开头和结尾的空格
    2. +/-号(可选)
    3. 数字
    4. .(可选)
    5. 一个点必须和至少一个数字相连,可前可后
    6. e(可选)
    7. 如果有e,后面必须跟整数,也就是+/-加数字,不能是小数
  3. 定义状态

这部分状态,官方题解可能更清晰一点

wxy_leetcode_2022-10-28_15-03-44

  1. 画出状态转移图

    起始的空格
    符号位
    整数部分
    左侧有整数的小数点
    左侧无整数的小数点(根据前面的第二条额外规则,需要对左侧有无整数的两种小数点做区分)
    小数部分
    字符 e e e
    指数部分的符号位
    指数部分的整数部分
    末尾的空格

  2. 编码

将状态转移图用字典表示,注意起始条件和结束条件

知识点

《深入浅出理解有限状态机》知乎云峰小罗

有限状态机:可归纳为4个要素,即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:
①现态:是指当前所处的状态。
②条件:又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

表示数值的字符串 - 表示数值的字符串 - 力扣(LeetCode)

如果输入的过程中某一步转移失败了,即不存在对应的「转移规则」,此时计算将提前中止。在这种情况下我们也判定该字符串「被拒绝」。

一个自动机,总能够回答某种形式的「对于给定的输入字符串 S,判断其是否满足条件 P」的问题。在本题中,条件 P 即为「构成合法的表示数值的字符串」。

自动机驱动的编程,可以被看做一种暴力枚举方法的延伸:它穷尽了在任何一种情况下,对应任何的输入,需要做的事情。

自动机在计算机科学领域有着广泛的应用。在算法领域,它与大名鼎鼎的字符串查找算法「KMP」算法有着密切的关联;在工程领域,它是实现「正则表达式」的基础。

class Solution:
    def isNumber(self, s: str) -> bool:
        # 官方这里用字典列表的方式很巧妙
        # none代表空格, flag代表正负号, num代表数字, dot代表小数点, e代表e/E
        trans = [
            {'none':0, 'flag':1, 'num':2, 'dot':4},
            {'num':2, 'dot':4},
            {'num':2, 'dot':3, 'e':6, 'none':9},
            {'num':5, 'e':6, 'none':9},
            {'num':5},
            {'num':5, 'e':6, 'none':9},
            {'flag':7, 'num':8},
            {'num':8},
            {'num':8, 'none':9},
            {'none':9}
        ]
        # 当前状态为0
        state = 0
        for i in s:
            if i in '0123456789':key = 'num'
            elif i in '+-':key = 'flag'
            elif i in 'eE':key = 'e'
            elif i=='.':key = 'dot'
            elif i==' ':key = 'none'
            else:key = 'unknown'
            if key not in trans[state].keys():return False
            state = trans[state][key]
        return state in [2,3,5,8,9]
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

piukaty

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

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

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

打赏作者

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

抵扣说明:

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

余额充值