LeetCode[929,930,931,932](周赛108)总结
本次周赛的地址为:https://leetcode-cn.com/contest/weekly-contest-108
文章目录
929. 独特的电子邮件地址
- 题目难度Easy
每封电子邮件都由一个本地名称和一个域名组成,以 @ 符号分隔。
例如,在 alice@leetcode.com
中, alice
是本地名称,而 leetcode.com
是域名。
除了小写字母,这些电子邮件还可能包含 ','
或 '+'
。
如果在电子邮件地址的本地名称部分中的某些字符之间添加句点('.'
),则发往那里的邮件将会转发到本地名称中没有点的同一地址。例如,"alice.z@leetcode.com”
和 “alicez@leetcode.com”
会转发到同一电子邮件地址。 (请注意,此规则不适用于域名。)
如果在本地名称中添加加号('+'
),则会忽略第一个加号后面的所有内容。这允许过滤某些电子邮件,例如 m.y+name@email.com
将转发到 my@email.com
。 (同样,此规则不适用于域名。)
可以同时使用这两个规则。
给定电子邮件列表 emails
,我们会向列表中的每个地址发送一封电子邮件。实际收到邮件的不同地址有多少?
示例:
输入:["test.email+alex@leetcode.com","test.e.mail+bob.cathy@leetcode.com","testemail+david@lee.tcode.com"]
输出:2
解释:实际收到邮件的是 "testemail@leetcode.com" 和 "testemail@lee.tcode.com"。
提示:
1 <= emails[i].length <= 100
1 <= emails.length <= 100
- 每封
emails[i]
都包含有且仅有一个'@'
字符。
解题思路
这道题可以是一个比较简单的字符串题。主要就是先以@
为间隔,将本地名称和域名分割开。然后把字符串的本地名称部分的.
给去掉,把+
号后面的字符删除。然后再和域名拼接到一起。判断是否已经出现过后,通过一个list进行存储。最后返回这个list的长度。
代码:
class Solution:
def numUniqueEmails(self, emails):
"""
:type emails: List[str]
:rtype: int
"""
list = []
for email in emails:
email1, email2 = email.split("@")
email1 = email1.split("+")[0]
email1 = email1.replace('.', '')
email = email1 + '@' + email2
if email not in list:
list.append(email)
return len(list)
其中,list的方式可以通过set()
来进行一定的精简。set
是Python中的一个类型,它不包含重复的元素,会自动替我们判断元素是否已出现,如果已经出现过了,则会自动将该元素删除。
优化代码:
class Solution:
def numUniqueEmails(self, emails):
"""
:type emails: List[str]
:rtype: int
"""
list = set()
for email in emails:
email1, email2 = email.split("@")
email1 = email1.replace('.', '').split("+")[0]
email = email1 + '@' + email2
list.add(email)
return len(list)
930. 和相同的二元子数组
- 题目难度Medium
在由若干 0
和 1
组成的数组 A
中,有多少个和为 S
的非空子数组。
示例:
输入:A = [1,0,1,0,1], S = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
提示:
A.length <= 30000
0 <= S <= A.length
A[i]
为0
或1
解题思路
这道题的整体思路其实非常简单,只需要遍历一次数组A
,然后将当前的子数组求和得到一个sum
。
- 如果
sum
<S
,则说明当前的子数组不够大,继续向后遍历。 - 如果
sum
==S
,则说明是符合条件的子数组,计数器加一。同时,因为子数组的头部会出现有多个0
元素的情况,而每去掉一个头部的0
元素,子数组的和sum
仍等于S
。因此,还需要统计子数组头部0
元素的个数。如果有n
个0
元素,则该情况下满足要求的子数组实际上有n+1
个。 - 如果
sum
>S
,则需要删除子数组第一个1
元素及其之前的全部元素。再根据其头部的0
元素个数进行子数组的数量统计。
代码:
class Solution:
def numSubarraysWithSum(self, A, S):
"""
:type A: List[int]
:type S: int
:rtype: int
"""
l = []
count = 0
sum = 0
for i in A:
l.append(i)
sum += i
if sum > S:
while l[0] == 0:
del l[0]
del l[0]
sum -= 1
if sum == S:
if 1 in l:
count += l.index(1) + 1
else:
count += len(l)
return count
通过阅读这次周赛第一名选手cai_lw的代码,学习到了一个更加简单的方式。
使用一个字典保存数组某个位置之前的数组和,然后遍历数组求和,这样当我们求到一个位置的和的时候,向前找sum-k
是否在数组中,如果在的话,更新结果为之前的结果+1。同时,当前这个sum
出现的次数就多了一次。
优化代码:
from collections import defaultdict
class Solution:
def numSubarraysWithSum(self, A, S):
"""
:type A: List[int]
:type S: int
:rtype: int
"""
d=defaultdict(lambda:0)
ps=0
ans=0
for a in A:
d[ps]+=1
ps+=a
ans+=d[ps-S]
return ans
还是不是很理解这个思路。回头继续思考。
931. 下降路径最小和
- 题目难度Medium
给定一个方形整数数组 A
,我们想要得到通过 A
的下降路径的最小和。
下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列。
示例:
输入:[[1,2,3],[4,5,6],[7,8,9]]
输出:12
解释:
可能的下降路径有:
[1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9]
[2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9]
[3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9]
和最小的下降路径是 [1,4,7]
,所以答案是 12
。
提示:
1 <= A.length == A[0].length <= 100
-100 <= A[i][j] <= 100
解题思路
这是一个动态规划的问题,我当时的思路是从第一层的节点开始,选择下一层最小可以接触到的最小节点进行连接,并迭代下去得到结果。
但是如果这样做的话,得到的效果其实只是从第一层节点向下降能得到的最小路径。
而在这个过程中,为了追求下层节点的最小值,而一起向值较小的下层节点靠拢,而忽略掉很多更优秀的路径。
错误代码:
class Solution:
def minFallingPathSum(self, A):
"""
:type A: List[List[int]]
:rtype: int
"""
so = float("inf")
for i in range(len(A[0])):
do = A[0][i]
index = i
for j in range(1, len(A)):
if index == 0:
temp = min(A[j][index], A[j][index + 1])
index = A[j][index : index + 2].index(temp)
do += temp
elif index == len(A[0]):
temp = min(A[j][index - 1], A[j][index])
index = A[j][index -1:].index(temp)
do += temp
else:
temp = min(A[j][index - 1:index + 2])
index = A[j][index -1:index + 2].index(temp)
do += temp
if do < so:
so = do
return so
而实际上这道动态规划题目的解题思路应该为,以下一层的节点为目标,获取到上一层可以接触到的三个相邻节点,并选择最小的值进行连接。这样做得到的结果就是:从第一层节点到当前位置的最小路径。
用图片来展示,即为下图:
正确代码:
class Solution(object):
def minFallingPathSum(self, A):
"""
:type A: List[List[int]]
:rtype: int
"""
n = len(A)
dp = A[0][::]
for row in A[1:]:
ndp = row[::]
for i in range(n):
candidates = [dp[i]]
if i:
candidates.append(dp[i-1])
if i != n-1:
candidates.append(dp[i+1])
ndp[i] += min(candidates)
dp = ndp
return min(dp)
932. 漂亮数组
- 题目难度Medium
对于某些固定的 N
,如果数组 A
是整数 1, 2, ..., N
组成的排列,使得:
对于每个 i < j
,都不存在 k
满足 i < k < j
使得 A[k] * 2 = A[i] + A[j]
。
那么数组 A
是漂亮数组。
给定 N
,返回任意漂亮数组 A
(保证存在一个)。
示例 1:
输入:4
输出:[2,1,4,3]
示例 2:
输入:5
输出:[3,1,2,5,4]
提示:
1 <= N <= 1000
解题思路
我没有理解的很透彻,因此就照搬他人的说法了。
可以先从题目的要求看:因为2*A[k]
是偶数,所以只需要保证A[i]
和A[j]
为奇数和偶数,就可以满足这个性质。
而如何在更长的数组中满足该性质呢?递归就可以了。其中有一个要点要注意。如果需要满足题目的要求,则奇数的数量总是和偶数相等,或是更多的。因此,在构建的过程中,记得将奇数的那一部分设置为可能更大的那一部分进行递归。
至于递归的边界条件N==1
时,其实令这个元素的值等于任意整数似乎都可以,比如5,虽然这样得到的数组元素值很大,但是依然满足条件。但是为了美观,选择令其为1
即可。
正确代码:
class Solution(object):
def beautifulArray(self, N):
"""
:type N: int
:rtype: List[int]
"""
if N==1:
return [1]
else:
l=self.beautifulArray(N//2)
r=self.beautifulArray(N-N//2)
return [x*2 for x in l]+[x*2-1 for x in r]