-
有限状态机
请你来实现一个 myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi
函数)。
函数 myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。 - 如果整数数超过 32 位有符号整数范围
[−2^31, 2^31−1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−2^31
的整数应该被固定为−2^31
,大于2^31−1
的整数应该被固定为2^31−1
。 - 返回整数作为最终结果。
步骤:1.写出状态转移表格,定义初始状态
2.获取当前状态
3.根据当前状态查表更新下一状态
'' +/- 0-9 other start start sign number end sign end end number end number end end number end end end end end end #力扣官方题解 INT_MAX = 2 ** 31 - 1 INT_MIN = -2 ** 31class Automaton:def __init__(self):self.state = 'start'self.sign = 1self.ans = 0self.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 0if c == '+' or c == '-':return 1if c.isdigit():return 2return 3def 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 -1class Solution:def myAtoi(self, str: str) -> int:automaton = Automaton() for c in str:automaton.get(c)return automaton.sign * automaton.ans
优化点:如果写在同个类里可以当状态更新为“end”时结束循环
-
正则表达式
请你来实现一个 myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi
函数)。
函数 myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。 - 如果整数数超过 32 位有符号整数范围
[−2^31, 2^31−1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−2^31
的整数应该被固定为−2^31
,大于2^31−1
的整数应该被固定为2^31−1
。 - 返回整数作为最终结果。
正则规则总结:
1.^:指定开头 ^s 匹配以s开头的字符串
2.$:指定结尾 aaa$匹配以aaa结尾的字符串
^ $同时使用:精准匹配 ^saaa$只能匹配saaa
3.转义序列需要添加\: \.匹配带.的字符串
4.字符簇[]: [a-zA-Z]匹配英文字符串
^[]同时使用:^表示“排除的意思” [^A-Z]匹配非大写字符串
5..可以匹配任何字符(\n \r除外) ^.a$匹配任意长度为2且以a结尾的字符串
6.{}限制出现次数:{m,n} 代表出现[m,n]次 a{3,}$匹配任意以3个及3个以上a结尾的字符串
简化表达:?={0,1} *={0,} +={1.}
7.
\d
,\w
,\s
- 匹配数字、字符、空格。
\D
,\W
,\S
- 匹配非数字、非字符、非空格题目需要的匹配规则:^[\+\-0-9]?\d* ^[\+\-]?\d+
import re class Solution:def myAtoi(self, str: str) -> int:INT_MIN = -2147483648INT_MAX = 2147483647 str = str.lstrip() #清除左边多余的空格num_rule = re.compile(r'^[\+\-]?\d+') #设置正则规则num = num_rule.findall(str) #查找匹配的内容#num = ['-123']num = int(*num) #解包并且转换成整数return max(min(num,INT_MAX),INT_MIN)
-
Manacher
Manacher算法:线性复杂度 O(2*len+3)——竞赛难度...别为难我了(哭
(44 封私信 / 80 条消息) 有什么浅显易懂的Manacher Algorithm讲解? - 知乎 (zhihu.com)
def manacher(s):T = '#'.join('^{}$'.format(s)) #原奇数长度的字符串插入偶数个字符长度仍为奇,原偶数长度的字符串插入奇数个字符长度变为奇,这样能保证子串长度只为奇;#由于边界符号不同,到边界时自动结束循环,减少了对越界条件的判断; P = [0] * len(T)R, C = 0, #使用中心下标C和尾部下标Rfor i in range(1,len(T) - 1): #以每个字符作为对称中心if i < R:P[i] = min(P[2 * C - i], R - i)while T[i+(P[i]+1)] == T[i-(P[i]+1)]:P[i] += 1if i + P[i] > R:R, C = i + P[i], ireturn P
-
动态规划
给你一个字符串 s
,请你统计并返回这个字符串中 回文子串 的数目。
动态规划三步骤:穷举分析—确定边界—确定最优子结构(找规律)
状态定义:dp[i, j]表示以第i位开头以第j位结尾的字符串是否是回文串
边界:i—[0, len(s)-1] j—[i,len(s)]
根据回文串性质可知:由 dp[i, j]== True 加上 s[i-1]==s[j+1] 可以推导出 dp[i-1,j+1]==True
最优子结构:dp[i, j] = s[i]==s[j] + dp[i+1,j-1]==True 所以对i遍历时需要倒序,对j遍历则需要正序;
但推导公式是向两边同时扩展得到,不适用于字符串初始长度为1/2的情况;
完整规律:i=j时,回文串成立;i=j-1时,回文串成立;其余情况,递推;
class Solution:def countSubstrings(self, s: str) -> int:dp = [[False] * len(s) for _ in range(len(s))]result = 0for i in range(len(s)-1, -1, -1): #i倒序遍历for j in range(i, len(s)): #j正序遍历if s[i] == s[j]:if j - i <= 1: #情况一和情况二result += 1dp[i][j] = Trueelif dp[i+1][j-1]: #情况三result += 1dp[i][j] = Truereturn result