刷题过程主要参考下面相关链接,所用语言为Python
注意:标题中的数字表示在LeetCode中的序号,后面的符号表示难度:E:easy,M:Medium,H:hard
每道题包括5个部分:题目,难度,方法,代码,复杂度,参考链接。
本文为字符串题目
回溯算法参考好文:https://mp.weixin.qq.com/s/tRNHG8Q6GZ_9Dfm2ljD_9w
Leetcode-字符串
1. 无重复字符的最长子串(3M)
难度:【中等】
方法:双指针 + sliding window
- 定义两个指针 start 和 end 得到 sliding window
- start 初始为0,用end线性遍历每个字符,用 recod 记录下每个字母最新出现的下标
- 两种情况:一种是新字符没有在 record 中出现过,表示没有重复,一种是新字符 char 在 record 中出现过,说明 start 需要更新,取 start 和 record[char]+1 中的最大值作为新的 start。
需要注意的是:两种情况都要对record进行更新,因为是新字符没在record出现过的时候需要添加到record中,而对于出现过的情况,也需要把record中对应的value值更新为新的下标。
参考:https://blog.csdn.net/qq_32424059/article/details/90708512
代码如下:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
record = {}
start,res = 0,0
for end in range(len(s)):
if s[end] in record:
start = max(start, record[s[end]] + 1)
record[s[end]] = end
res = max(res, end - start + 1)
return res
时间复杂度:O(n)
空间复杂度:O(1)
2. 最长回文子串(5M)
难度:【中等】
方法:使用中心扩展法
遍历每一个字符串,分两种情况进行中心扩展,一种为奇数,一种为偶数,如果两边字母相同,就继续扩展;如果不相同就停止扩展,对所有满足要求的长度求出最大值,得到最终答案。
代码如下:
class Solution:
def expandAroundCenter(self, s, left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return left + 1, right - 1
def longestPalindrome(self, s: str) -> str:
start, end = 0, 0
for i in range(len(s)):
left1, right1 = self.expandAroundCenter(s, i, i)
left2, right2 = self.expandAroundCenter(s, i, i + 1)
if right1 - left1 > end - start:
start, end = left1, right1
if right2 - left2 > end - start:
start, end = left2, right2
return s[start: end + 1]
时间复杂度:O(n^2)
空间复杂度:O(1)
3. 字符串转换整数 (atoi)(8M)
https://leetcode-cn.com/problems/string-to-integer-atoi/
难度:【中等】
方法:使用正则表达式
^:匹配字符串开头
[+-]:代表一个+字符或-字符
?:前面一个字符可有可无
\d:一个数字
+:前面一个字符的一个或多个
\D:一个非数字字符
*:前面一个字符的0个或多个
# 使用正则表达式
class Solution:
def myAtoi(self, s: str) -> int:
INT_MAX = 2**31 - 1
INT_MIN = -2**31
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) #返回值
# 一行代码搞定,和上面的一样
class Solution:
def myAtoi(self, s: str) -> int:
return max(min(int(*re.findall('^[\+\-]?\d+', s.lstrip())), 2**31 - 1), -2**31)
时间复杂度:O(n)
空间复杂度:O(1)
4. 电话号码的字母组合(17M)
难度:【中等】
方法一:回溯
- 首先使用哈希表存储每个数字对应的多有可能的字母,然后进行回溯操作
- 每次取电话号码的一位数字,进行回溯,直到遍历完所有的数字
代码如下:
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return list()
phoneMap = {
'2':'abc',
'3':'def',
'4':'ghi',
'5':'jkl',
'6':'mno',
'7':'pqrs',
'8':'tuv',
'9':'wxyz'
}
def backtrack(index):
if index == len(digits):
combinations.append(''.join(combination))
else:
digit = digits[index]
for letter in phoneMap[digit]:
combination.append(letter)
backtrack(index + 1)
combination.pop()
combination = []
combinations = []
backtrack(0)
return combinations
时间复杂度: O ( 3 m × 4 n ) O\left(3^{m} \times 4^{n}\right) O(3m×4n)
空间复杂度: O ( m + n ) O(m+n) O(m+n)
5. 有效的括号(20E)
难度:【简单】
方法:使用栈
可以先判断长度是不是偶数,如果不是偶数可以直接False
处理右括号需要看前面有没有匹配的左括号,所以可以用栈。
每次左括号就压进栈,右括号就看栈顶是不是匹配的左括号,如果不是说明有问题, 比如 [ ),就无法匹配上,如果匹配上了,就把栈顶pop掉。
最后判断是不是栈里所有左括号都脱单了。
参考:https://blog.csdn.net/qq_32424059/article/details/90748913
代码如下:
class Solution:
def isValid(self, s: str) -> bool:
if len(s) % 2 ==1:
return False
stack = []
pairs = {']':'[',
')':'(',
'}':'{'}
for i in s:
if i in pairs:
if not stack or stack[-1] != pairs[i]:
return False
stack.pop()
else:
stack.append(i)
return not stack
时间复杂度:O(n)
空间复杂度:O(n+∣Σ∣),Σ 为 6
6. 编辑距离(72H)
难度:【困难】
方法:动态规划
d p [ i ] [ j ] dp[i][j] dp[i][j] 代表 word1 到 i 位置转换成 word2 到 j 位置需要最少步数
所以,
当 w o r d 1 [ i ] = = w o r d 2 [ j ] , d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] word1[i] == word2[j],dp[i][j] = dp[i-1][j-1] word1[i]==word2[j],dp[i][j]=dp[i−1][j−1];
当 w o r d 1 [ i ] ! = w o r d 2 [ j ] , d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + 1 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 word1[i]!=word2[j],dp[i][j]=min(dp[i−1][j−1],dp[i−1][j],dp[i][j−1])+1
其中, d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1] 表示替换操作, d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 表示删除操作, d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j−1] 表示插入操作。
PS:需要特别注意的是,word的索引和dp的索引是差一个的,所以代码中的写法是
w
o
r
d
1
[
i
−
1
]
=
=
w
o
r
d
2
[
j
−
1
]
word1[i-1] == word2[j-1]
word1[i−1]==word2[j−1]
上图中:
第一行,是 word1 为空变成 word2 最少步数,就是插入操作
第一列,是 word2 为空,需要的最少步数,就是删除操作
考点:
- 能否理解编辑距离的含义,发现子问题,写出状态转移方程
- 能否根据状态转移方程写出递归的解法
- 能否意识到递归的局限性
- 能否用进行优化
参考:
https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-by-leetcode-solution/
https://leetcode-cn.com/problems/edit-distance/solution/zi-di-xiang-shang-he-zi-ding-xiang-xia-by-powcai-3/
代码如下:
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m = len(word1) + 1
n = len(word2) + 1
dp = [[0] * n for i in range(m)]
for i in range(n):
dp[0][i] = i
for j in range(m):
dp[j][0] = j
for i in range(1,m):
for j in range(1,n):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j])+1
return dp[-1][-1]
时间复杂度:O(mn)
空间复杂度:O(mn)
有效的字母异位词(242E)
难度:【简单】
三种方法:第一种是直接使用python 的 sorted函数,对字符串进行排序后,进行比较判断是都相同;第二种是使用 hashmap,将两个字符串分别统计次数放到字典中后,进行比较判断是否相同;第三种是构建一个存放26个字母的数组,统计次数,相比第二种省内存,也是最优方案。
方法一:直接使用python 的 sorted函数(也可以使用Collections中的Counter方法,直接比较)
代码如下:
from collections import Counter
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
s_ = Counter(s)
t_ = Counter(t)
return s_ == t_
时间复杂度:O(nlogn)
空间复杂度:O(n)
方法二:使用 hashmap
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
dic_1 = {}
dic_2 = {}
for i in s:
dic_1[i] = dic_1.get(i,0) + 1
for j in t:
dic_2[j] = dic_2.get(j,0) + 1
return dic_1 == dic_2
时间复杂度:O(n)
空间复杂度:O(n)
方法三:构建一个存放26个字母的数组,统计次数
class Solution(object):
def isAnagram(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
temp = [0] * 26
for i in range(len(s)):
temp[ord(s[i]) - ord('a')] += 1
for j in range(len(t)):
temp[ord(t[j]) - ord('a')] -= 1
for k in range(26):
if temp[k] != 0:
return False
else:
return True
时间复杂度:O(n)
空间复杂度:O(1)
7. 最长公共子序列(1143M)
难度:【中等】
方法:动态规划
代码如下:
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m = len(text1)
n = len(text2)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1,m+1):
for j in range(1,n+1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j],dp[i][j-1])
return dp[m][n]
时间复杂度:O(mn)
空间复杂度:O(mn)