题目描述

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,“123” 123, “0032” 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为  ,大于  的整数应该被固定为 231 − 1 。
  6. 返回整数作为最终结果。

注意:

  • 本题中的空白字符只包括空格字符 ' ' 。
  • 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

思路

最开始是用大模拟的想法做的模拟,后来看官方题解似乎可以抽象成一个有限状态自动机,对应于状态模式 ,官方答案构建了如图的自动机模型:

也可以用下面的表格表示:

’ ’+/-numberother
startstartsignedin_numberend
signedendendin_numberend
in_numberendendin_numberend
endendendendend

官方答案构建了一个自动机类:

INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31
 
class Automaton:
    def __init__(self):
        self.state = 'start'
        self.sign = 1
        self.ans = 0
        self.table = {
            'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end'],
        }
        
    def get_col(self, c):
        if c.isspace():
            return 0
        if c == '+' or c == '-':
            return 1
        if c.isdigit():
            return 2
        return 3
 
    def get(self, c):
        self.state = self.table[self.state][self.get_col(c)]
        if self.state == 'in_number':
            self.ans = self.ans * 10 + int(c)
            self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
        elif self.state == 'signed':
            self.sign = 1 if c == '+' else -1
 
class Solution:
    def myAtoi(self, str: str) -> int:
        automaton = Automaton()
        for c in str:
            automaton.get(c)
        return automaton.sign * automaton.ans

其中 get 函数会读取一个字符并根据状态进行转移,get_col 函数判断字符并返回(表格所对应的)列数。

想得太多

看了一下评论区,Edward Elric 提到 65. 有效数字393. UTF-8 编码验证 都是类似的自动机题目,可以注意一下。

代码

模拟

class Solution:
    def myAtoi(self, s: str) -> int:
        s = s.lstrip()
        # print(s)
        sign_flag = True
        ret = 0
        for i, ch in enumerate(s):
            if ch == '-':
                if i == 0:
                    sign_flag = False
                    continue
                else:
                    break
            if ch == '+':
                if i == 0:
                    continue
                else:
                    break
            if ord(ch)>=ord('0') and ord(ch)<=ord('9'):
                ret = ret * 10 + (ord(ch)-ord('0'))
                if ret > ((1<<31)-1) and sign_flag is True:
                    ret = (1<<31)-1
                    break
                elif ret > (1<<31) and sign_flag is False:
                    ret = 1<<31
                    break
            else:
                break
        if sign_flag is False:
            ret = -ret
        return ret