来源:LeetCode
难度:中等
问题详情:
由于力扣本身题目描述的不够清楚,因此接下来使用我自己的话叙述该问题。
给定一个字符串s
,读取其中的整数,但必须是以下形式:
“ ”
可以忽略,“+”
字符代表是正整数,"-"
字符代表是负整数,如果没有这二者则默认为正符号;0
案例:
输入:
“”
输出:0
解释:没有读到数字
输入:
“+1”
输出:1
输入:
“+1+1”
输出:1
解释:读取到第二个“+”
时根据第2个条件,停止读取
输入:
“ +1+”
输出:1
解释:根据第1条,第2条返回1
输入:
“ ++1+”
输出:0
解释:根据第1条,第2条,第4条,返回为0
输入:
“2147483648”
输出:”2147483647“
解释:根据第3条,超界限
在真正开始介绍各种算法前,先以表格形式展示各自的时间复杂度和空间复杂度,其中 n n n表示字符串 s s s的长度。
算法 | 时间复杂度 | 空间复杂度 |
---|---|---|
直接法 | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) |
正则表达式法 | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) |
自动机 | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) |
流程如下:
”+“
或者”-“
, 则改变符号变量sign
的值由于之前的问题详情里,描述的还算详细,因此直接给出代码:
def myAtoi(s: str) -> int:
s = s.lstrip() # 去左边空格
sign = 1
num = 0
MAX = 2 ** 31 - 1
MIN = -2 ** 31
i = 0 # 字符索引
index = 0 # 数字索引
# 排除空字符串
if not s:
return 0
# 符号
if s[0] == '+':
i += 1
elif s[0] == '-':
sign = -1
i += 1
while i < len(s):
if s[i].isnumeric():
index += 1
num = num * 10 + eval(s[i])
# 数字长度超过或者等于10,则要考虑是否超过了界限
if index >= 10:
if num * sign >= MAX:
return MAX
elif num * sign <= MIN:
return MIN
i += 1
else:
break
return sign * num
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)
流程如下:
首先去除字符左边的空格
使用正则表达式,去匹配满足以下两种形式的一种:
(1)正负号开头+数字
(2)纯数字
将上述的结果转为int
然后将int类型的数值限定在32位范围内。
def myAtoi2(s: str) -> int:
import re
INT_MAX = 2147483647
INT_MIN = -2147483648
s = s.lstrip() #清除左边多余的空格
num_re = re.compile(r'^[\+\-]?\d+') #设置正则规则
num = num_re.findall(s) #查找匹配的内容
num = int(*num) #由于返回的是个列表,解包并且转换成整数
return max(min(num,INT_MAX),INT_MIN) #返回值
r'^[\+\-]?\d+'
中的:
‘^’
表示以后面的字符开头'[\+\-]'
表示正号或者负号的一种,‘\’
表示转义字符'?'
表示匹配前面的字符串0次或者1次,也就是说前面的正负号只能出现0次或者1次‘\d+’
中‘\d’
表示匹配0-9
,而'+'
表示匹配前面的字符串1次或者多次可以得知,这样的匹配规则得到的字符串只能是一个或者没有找到,所以findall
的返回结果num
只能是有一个字符串的列表或者空列表。
对于int(*num)
:
num
为一个字符串的列表时,比如['-123'],
那int(*num)
就是int('-123')=123
num
为空列表时,int(*num)=int()
,int()
默认为0
然后使用min()
、max()
限定范围。
正则表达式法的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)
自动机是给定符号输入,依据(可表达为一个表格的)转移函数“跳转”过一系列状态的一种机器。
我们根据第1节中的条件,构建如下图中的状态转换。图片来源。
共有如下状态:
start
;end
;signed
;in_number
;以start->signed
为例,只有在开始阶段读取到正负号才进入signed
状态。
同样地,使用如下表格也可以表示状态转移函数(表格来源),符号有空、正负号、数字、其它四种。
接下来,就是使用代码实现这个状态转移函数:
table
构建如上的表格;table[‘开始状态’]['列索引']
得到下一个转移后的状态,初始状态为‘start’
;get_col
);in_number
时,判断是否越界;‘signed’
时,则重新赋值正负号;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)] # table[开始状态][列索引]
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
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)