LeetCode6-10
LeetCode: https://leetcode-cn.com/problemset/all/.
6.Z字形变换
难度:中等
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
示例1:
输入:s = “PAYPALISHIRING”, numRows = 3
输出:“PAHNAPLSIIGYIR”
示例2:
输入:s = “PAYPALISHIRING”, numRows = 4
输出:“PINALSIGYAHRPI”
方法1(按行排序):
从左到右迭代
s
s
s,将每个字符添加到合适的行。
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1:
return s
convStr = ""
result = ["" for _ in range(numRows)]
for index, char in enumerate(s):
subIndex = index % (2 * numRows - 2)
if subIndex < numRows:
result[subIndex] += char
else:
result[2 * (numRows - 1) - subIndex] += char
for each in result:
convStr += each
return convStr
方法2(按行访问):
首先访问行0中的所有字符,接着访问行1,然后行2,依此类推…
对于所有整数
k
k
k,
- 行0中的字符位于索引 k ( 2 n u m R o w s − 2 ) k(2numRows- 2) k(2numRows−2)处;
- 行 n u m R o w s − 1 numRows−1 numRows−1中的字符位于索引 k ( 2 n u m R o w s − 2 ) + n u m R o w s − 1 k(2numRows−2)+numRows−1 k(2numRows−2)+numRows−1处;
- 内部的行 i i i中的字符位于索引 k ( 2 n u m R o w s − 2 ) + i k(2numRows−2)+i k(2numRows−2)+i以及 ( k + 1 ) ( 2 n u m R o w s − 2 ) − i (k+1)(2numRows−2)−i (k+1)(2numRows−2)−i处;
时间复杂度: O ( n ) O(n) O(n),空间复杂度: O ( n ) O(n) O(n)
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1:
return s
convStr = ""
lens=len(s)
cycleLen =2*numRows-2
for i in range(numRows):
for j in range(0,lens-i,cycleLen):
convStr+=s[j+i]
if (i!=0 and i!=numRows-1 and j+cycleLen-i<lens):
convStr+=s[j+cycleLen-i]
return convStr
7.整数反转
难度:简单
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围
[
−
2
31
,
2
31
−
1
]
[−2^{31}, 2^{31} − 1]
[−231,231−1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例1:
输入:x = 123
输出:321
示例2:
输入:x = -123
输出:-321
示例3:
输入:x = 120
输出:21
示例4:
输入:x = 1534236469
输出:0
方法1(转字符串):
但用到了64位整数。
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def reverse(self, x: int) -> int:
string=str(abs(x))
string=string[::-1]
revNum=int(string)
if revNum>2**31:return 0
return -revNum if x<0 else revNum
方法2(数学):
记
r
e
v
rev
rev为翻转后的数字,为完成翻转,我们可以
// 弹出 x 的末尾数字 digit
digit = x % 10
x /= 10
// 将数字 digit 推入 rev 末尾
rev = rev * 10 + digit
我们需要在推入数字之前,判断是否满足
−
2
31
≤
r
e
v
⋅
10
+
d
i
g
i
t
≤
2
31
−
1
-2^{31}\le rev\cdot10+digit\le2^{31}-1
−231≤rev⋅10+digit≤231−1,
若该不等式不成立则返回
0
0
0。
但是题目要求不允许使用64 位整数,上式改为
⌈
−
2
31
10
⌉
≤
r
e
v
≤
⌊
2
31
−
1
10
⌋
。
\left\lceil\frac{-2^{31}}{10}\right\rceil \leq r e v \leq\left\lfloor\frac{2^{31}-1}{10}\right\rfloor。
⌈10−231⌉≤rev≤⌊10231−1⌋。证明
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def reverse(self, x: int) -> int:
INT_MIN, INT_MAX = -2**31, 2**31 - 1
rev = 0
while x != 0:
# INT_MIN 也是一个负数,不能写成 rev < INT_MIN // 10
if rev < INT_MIN // 10 + 1 or rev > INT_MAX // 10:
return 0
digit = x % 10
# Python3 的取模运算在 x 为负数时也会返回 [0, 9) 以内的结果,因此这里需要进行特殊判断
if x < 0 and digit > 0:
digit -= 10
# 同理,Python3 的整数除法在 x 为负数时会向下(更小的负数)取整,因此不能写成 x //= 10
x = (x - digit) // 10
rev = rev * 10 + digit
return rev
8. 字符串转换整数 (atoi)
难度:中等
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
- 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
- 返回整数作为最终结果。
示例1:
输入:s = “42”
输出:42
示例2:
输入:s = " -42"
输出:-42
示例3:
输入:s = “4193 with words”
输出:4193
示例4:
输入:s = “words and 987”
输出:0
示例5:
输入:s = “-91283472332”
输出:-2147483648
方法1(暴力求解):
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
1
)
O(1)
O(1)
class Solution:
def myAtoi(self, s: str) -> int:
maxs = (1 << 31) - 1
mins = -1 * (1 << 31)
s = s.lstrip()
kun = len(s)
ints = 0
i = 0
k = 0
if kun != 0:
if s[i] == '+':
k = 1
i = i + 1
elif s[i] == '-':
k = -1
i = i + 1
else:
k = 1
while i < kun:
if (s[i] < '0') or (s[i] > '9'):
return max(min(ints, maxs), mins)
ints = ints * 10 + k * (int(s[i]))
i = i + 1
return max(min(ints, maxs), mins)
方法2(有限自动机):
我们的程序在每个时刻有一个状态 s,每次从序列中输入一个字符 c,并根据字符 c 转移到下一个状态 s’。这样,我们只需要建立一个覆盖所有情况的从 s 与 c 映射到 s’ 的表格即可解决题目中的问题。
我们可以用下面的表格来表示这个自动机:
’ ’ | +/- | number | other | |
---|---|---|---|---|
start | start | signed | in_number | end |
signed | end | end | in_number | end |
in_number | end | end | in_number | end |
end | end | end | end | end |
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
方法3(正则表达式):
import re
class Solution:
def myAtoi(self, s: str) -> int:
return max(min(int(*re.findall('^[\+\-]?\d+', s.lstrip())), 2**31 - 1), -2**31)
9. 回文数
难度:简单
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
- − 2 31 ≤ x ≤ 2 31 − 1 -2^{31} \le x \le 2^{31} - 1 −231≤x≤231−1
示例1:
输入:x = 121
输出:true
示例2:
输入:x = -121
输出:false
示例3:
输入:x = 10
输出:false
方法1(转字符串):
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def isPalindrome(self, x: int) -> bool:
if x<0:return False
s=str(x)
return s[::-1]==s
方法2(反转一半数字):
时间复杂度:
O
(
l
o
g
n
)
O(log n)
O(logn),空间复杂度:
O
(
1
)
O(1)
O(1)
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0 or (x % 10 == 0 and x != 0):
return False
revertedNumber = 0
while x > revertedNumber:
revertedNumber = revertedNumber * 10 + x % 10
x //= 10
return x == revertedNumber or x == revertedNumber // 10
10. 正则表达式匹配
难度:困难
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘
∗
\ast
∗’ 的正则表达式匹配。
- ‘.’ 匹配任意单个字符
- ‘ ∗ \ast ∗’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
示例1:
输入:s = “aa” p = “a”
输出:false
示例2:
输入:s = “aa” p = “a ∗ \ast ∗”
输出:true
示例3:
输入:s = “ab” p = “. ∗ \ast ∗”
输出:true
示例4:
输入:s = “aab” p = “c ∗ \ast ∗a ∗ \ast ∗b”
输出:true
示例5:
输入:s = “mississippi” p = “mis ∗ \ast ∗is ∗ \ast ∗p ∗ \ast ∗.”
输出:false
import re
class Solution:
def isMatch(self, s: str, p: str) -> bool:
return s in re.findall(p,s)
方法1(动态规划):
字母 + 星号的组合在匹配的过程中,本质上只会有两种情况:
- 匹配 s 末尾的一个字符,将该字符扔掉,而该组合还可以继续进行匹配;
- 不匹配字符,将该组合扔掉,不再进行匹配。
状态转移方程:
f
[
0
]
[
0
]
=
t
r
u
e
,
f
[
i
]
[
j
]
=
{
if
(
p
[
j
]
≠
′
∗
′
)
=
{
f
[
i
−
1
]
[
j
−
1
]
,
matches
(
s
[
i
]
,
p
[
j
]
)
false,
otherwise
otherwise
=
{
f
[
i
−
1
]
[
j
]
or
f
[
i
]
[
j
−
2
]
,
matches
(
s
[
i
]
,
p
[
j
−
1
]
)
f
[
i
]
[
j
−
2
]
,
otherwise
\begin{array}{ll} f[0][0]=&true,\\ f[i][j]=&\left\{\begin{array}{ll} \text { if }\left(p[j] \neq^{'\ast'}\right) & = \left\{\begin{array}{ll} f[i-1][j-1], & \text { matches }(s[i], p[j]) \\ \text { false, } & \text { otherwise } \\ \end{array}\right.\\ \text { otherwise }& = \left\{\begin{array}{ll} f[i-1][j] \text { or } f[i][j-2], & \text { matches }(s[i], p[j-1])\\ f[i][j-2],& \text { otherwise } \end{array}\right. \end{array}\right.\\ \end{array}
f[0][0]=f[i][j]=true,⎩⎪⎪⎨⎪⎪⎧ if (p[j]=′∗′) otherwise ={f[i−1][j−1], false, matches (s[i],p[j]) otherwise ={f[i−1][j] or f[i][j−2],f[i][j−2], matches (s[i],p[j−1]) otherwise
其中
matches
(
x
,
y
)
\text {matches}(x, y)
matches(x,y)判断两个字符是否匹配的辅助函数。只有当 y 是 . 或者 x 和 y 本身相同时,这两个字符才会匹配。
时间复杂度: O ( m n ) O(mn) O(mn),空间复杂度: O ( m n ) O(mn) O(mn)
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s), len(p)
def matches(i: int, j: int) -> bool:
if i == 0:
return False
if p[j - 1] == '.':
return True
return s[i - 1] == p[j - 1]
f = [[False] * (n + 1) for _ in range(m + 1)]
f[0][0] = True
for i in range(m + 1):
for j in range(1, n + 1):
if p[j - 1] == '*':
f[i][j] |= f[i][j - 2]
if matches(i, j - 1):
f[i][j] |= f[i - 1][j]
else:
if matches(i, j):
f[i][j] |= f[i - 1][j - 1]
return f[m][n]