目录
注意:
主要和b站大雪菜一起刷题,宝藏up主(https://www.bilibili.com/video/BV1Vt411M741)
38. 外观数列
思路:
- 这道题是个模拟题
- 每一轮循环寻找连续字符
- 注意循环是n-1
class Solution:
def countAndSay(self, n: int) -> str:
res = "1"
for i in range(n-1):
tmp = ""
j = 0
while j<len(res):
k = j
while (k<len(res)) and (res[k]==res[j]):
k += 1
tmp += str(k-j)+res[j]
j = k
res = tmp
return res
49. 字母异位词分组
思路:
- 字母的异位词,我们要找到它们的共同点
- 可以对每个词进行排序,字母相同的词排序结果是一样的,就可以进行分组
- python对字符串进行sorted之后是个list,不是str类型,需要处理
- 排序的总时间O(nmlogm+n)n个词,m为长度
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
dic = {}
for s in strs:
tmp = s
tmp = ''.join(sorted(tmp))
# print(tmp)
dic[tmp] = dic.get(tmp, []) + [s]
res = []
for v in dic.values():
res.append(v)
return res
151. 翻转字符串里的单词
思路:
- 可以每个单词进行翻转,再对整个字符串翻转
- 这里用python的strip、split,会更方便一点
class Solution:
def reverseWords(self, s: str) -> str:
s = s.strip()
s = s.split(' ')
i = 0
while i<len(s):
if s[i] == '':
s.pop(i)
continue
i += 1
return ' '.join(s[::-1])
165. 比较版本号
思路:
- 将版本号根据'.'分隔开,依次比较,若相等则继续比较下一段
- 若不相等直接比较大小返回1或-1
class Solution:
def compareVersion(self, version1: str, version2: str) -> int:
i,j=0,0
while i<len(version1) or j<len(version2):
x,y = i, j
while x<len(version1) and version1[x] != '.':
x += 1
while y<len(version2) and version2[y] != '.':
y += 1
a = 0 if x==i else int(version1[i:x])
b = 0 if y==j else int(version2[j:y])
if a<b:
return -1
if a>b:
return 1
i = x+1
j = y+1
return 0
929. 独特的电子邮件地址
思路:
- 用find函数,find(str,begin,end)找到‘@’的下标
- 将email地址分割成name+domain,然后对name进行单独的处理,再与‘@’和domain组合获得新的邮件地址,得到最后的答案
- 用set去重存储所有的答案,返回set的长度即可
class Solution:
def numUniqueEmails(self, emails: List[str]) -> int:
res = set()
for email in emails:
at = email.find('@')
name = ""
for c in email[:at]:
if c == '+':
break
elif c != '.':
name += c
domain = email[at+1:]
s = name+"@"+domain
res.add(s)
return len(res)
5. 最长回文子串
思路:
- 枚举每一个可能的中心点,分别用j,k两个指针向两边扩展,所指指针相等则继续移动,不相等则跳出判断是不是最长的
- 中心点可能是两个(回文子串长度为偶数),也可能是一个(回文子串长度为奇数)
- 时间复杂度为O(n^2),n为s的长度
class Solution:
def longestPalindrome(self, s: str) -> str:
max_res = ""
for i in range(len(s)):
j,k=i,i
# 回文串长度为奇数
while j>=0 and k<len(s) and s[j] == s[k]:
j -= 1
k += 1
tmp = s[j+1:k]
if len(tmp) > len(max_res):
max_res = tmp
j,k=i,i+1
# 回文串长度为偶数
while j>=0 and k<len(s) and s[j] == s[k]:
j -= 1
k += 1
tmp = s[j+1:k]
if len(tmp) > len(max_res):
max_res = tmp
return max_res
6. Z 字形变换
思路:
- 这题主要是找规律,第一排和最后一排都是首项为i,公差为2*(numrows-1)
- 中间的部分是两个等差数列交叉出现
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows==1:
return s
res = ""
for i in range(numRows):
# 第一行或者最后一行
if i==0 or i==numRows-1:
j = i
while j<len(s):
res += s[j]
j += 2*(numRows-1)
else:# 其余行
j,k=i,2*(numRows-1)-i
while j<len(s) or k<len(s):
res += s[j] if j<len(s) else ""
res += s[k] if k<len(s) else ""
j += 2*(numRows-1)
k += 2*(numRows-1)
return res
3. 无重复字符的最长子串
思路:
- 利用双指针,i代表当前无重复字符子串的头,j代表当前无重复字符子串的尾
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
i = 0
max_len = 1
for j in range(1,len(s)):
while s[j] in s[i:j]:
i += 1
if j-i+1>max_len:
max_len = j-i+1
return max_len
208. 实现 Trie (前缀树)
思路:
- 前缀树的结构就是,根节点为空,每一个节点有26个子节点,出现那个字符就将对应位置的指针赋予它
- 每一个节点还要标记下当前节点是不是某个单词的结尾
class Node:
def __init__(self):
self.is_end = False
self.son = [None]*26
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.root = Node()
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
p = self.root
for ch in word:
index = ord(ch) - ord('a')
if not p.son[index]:
p.son[index] = Node()
p = p.son[index]
p.is_end = True
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
p = self.root
for ch in word:
index = ord(ch) - ord('a')
if not p.son[index]:
return False
p = p.son[index]
return p.is_end
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
p = self.root
for ch in prefix:
index = ord(ch) - ord('a')
if not p.son[index]:
return False
p = p.son[index]
return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
273. 整数转换英文表示
思路:
- 这题主要是比较麻烦,但是想清楚就会好一点。
- 英文中是三个数字三个数字为一部分,__billion__million__thousand__,其中__为1~999,最后一个为0~999
- 对于1000以内的数写一个表达函数,然后套入上面这个形式就可以
class Solution:
def __init__(self):
self.small = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven"
"Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
"Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"]
self.decade = ["", "", "Twenty", "Thirty", "Fourty", "Fifty", "Sixty", "Seventy",
"Eighty", "Ninety"]
self.big = ["", "Thousand", "Million", "Billion"]
def numberToWords(self, num: int) -> str:
if num == 0:
return self.small[0]
i,j = 1000000000, 3
res = ""
while i and j>=0:
if num>i:
res += self.get_part(num//i) + self.big[j]
i //= 1000
j -= 1
res = res.strip()
return res
# 1000以内表达形式
def get_part(self, num):
res = ""
if num > 100:
res += self.small[num//100] + " Hundred "
num %= 100
if not num:
return res
if num > 20:
res += self.decade[num//10] + ' '
num %= 10
if not num:
return res
else:
res += self.small[num] + ' '
return res
166. 分数到小数
思路:
- 又是一道模拟题,要把步骤捋清楚
- 首先判断分子分母是不是负数,是的话需要进行处理成正数,同时记录下结果是否要添加负号
- 然后来计算,首先整除了没有余数了就可以直接输出答案
- 若是余数不为0说明还有小数部分,小数部分的计算就和手算步骤一样,关键是如何判断是循环节
- 用一个dic哈希表储存当前数字的位置,若有一个数字之前存在过,则后面的余数会是一样的,就可以得出循环节
class Solution:
def fractionToDecimal(self, numerator: int, denominator: int) -> str:
minus = False
if numerator<0:
minus = not minus
numerator = -numerator
if denominator<0:
minus = not minus
denominator = -denominator
res = str(numerator//denominator)
numerator = numerator%denominator
# 整除
if not numerator:
if minus and res!="0":
res = "-"+res
return res
# 计算小数部分
res += "."
dic = {}
while numerator:
if dic.get(numerator,0):
res = res[:dic[numerator]] + "(" +res[dic[numerator]:] + ")"
break
else:
dic[numerator] = len(res)
numerator *= 10
res += str(numerator//denominator)
numerator %= denominator
if minus:
res = "-"+res
return res
131. 分割回文串
思路:
- l这道题利用dfs+回溯遍历所有方案即可
- 写个判断回文串的函数
class Solution:
def partition(self, s: str) -> List[List[str]]:
res = []
self.dfs("", 0, s, res, [])
return res
def dfs(self, now, cur, s, ans, path):
if cur == len(s):
if self.check(now):
path.append(now)
ans.append(path[:])
path.pop()
return
if self.check(now):
path.append(now)
self.dfs("", cur, s, ans, path)
path.pop()
self.dfs(now+s[cur], cur+1, s, ans, path)
def check(self, s):
if not s:
return False
i,j=0, len(s)-1
while i<j:
if s[i]!=s[j]:
return False
i += 1
j -= 1
return True
227. 基本计算器 II
思路:
- 又是一道想起来比较复杂的题,由于有加减乘除的符号,考虑符号的优先级,可以用两个栈,一个存储符号,一个存储数字
- 若是数字就要进行计算,查看栈顶,是乘除符号则直接计算,若是加减符号则判断下栈里的加减符号有没有超过两个,(a+b+c*d)这里可以发现a+b可以先计算的。
- 在原本数字的末尾添加+0方便栈最后进行计算
class Solution:
def calculate(self, s: str) -> int:
op, num = [], []
s += "+0"
i = 0
while i<len(s):
# 空格跳过
if s[i] == ' ':
i += 1
continue
# 符号
if s[i] == '+' or s[i] == '-' or s[i] == '*' or s[i] == '/':
op.append(s[i])
else:
# 获取数字
j = i
while j<len(s) and ('0'<=s[j]<='9'):j+=1
num.append(int(s[i:j]))
i = j-1
# 操作符栈非空
if op:
if op[-1]=='*' or op[-1]=='/':
x = num.pop()
y = num.pop()
if op[-1]=='*':
num.append(x*y)
else:
num.append(y//x)
op.pop()
elif len(op)>=2:
c = num.pop()
b = num.pop()
a = num.pop()
op2 = op.pop()
op1 = op.pop()
if op1 == '+':
num.append(a+b)
else:
num.append(a-b)
num.append(c)
op.append(op2)
i += 1
num.pop()
# print(num)
return num[-1]