[英雄星球六月集训LeetCode解题日报] 第四日 贪心
一、 1221. 分割平衡字符串
链接: 1221. 分割平衡字符串
1. 题目描述
在一个 平衡字符串 中,‘L’ 和 ‘R’ 字符的数量是相同的。
给你一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
注意:分割得到的每个字符串都必须是平衡字符串,且分割得到的平衡字符串是原平衡字符串的连续子串。
返回可以通过分割得到的平衡字符串的 最大数量 。
来源:力扣(LeetCode)
2. 思路分析
LR换成括号,用栈的思想计数L或R的的数量即可,每次计数为0代表去掉一个独立括号单元。O(n),n为字符串长度
3. 代码实现
class Solution:
def balancedStringSplit(self, s: str) -> int:
cnt = 0
# 栈
l = 0
for i in s:
if i == 'L':
l += 1
else:
l -= 1
if l == 0:
cnt += 1
return cnt
二、 1217. 玩筹码
链接: 1217. 玩筹码
1. 题目描述
有 n 个筹码。第 i 个芯片的位置是 position[i] 。
我们需要把所有筹码移到同一个位置。在一步中,我们可以将第 i 个芯片的位置从 position[i] 改变为:
position[i] + 2 或 position[i] - 2 ,此时 cost = 0
position[i] + 1 或 position[i] - 1 ,此时 cost = 1
返回将所有筹码移动到同一位置上所需要的 最小代价
2. 思路分析
- 题意稍乱,其实是移动每个筹码时,偶数步算0代价,奇数步算1代价。
- 那么一定可以用0代价把所有筹码移动到相邻的两列上,最后移动少的那列即可。
- 也就是奇数偶数分别计数,要小的那个。
3. 代码实现
class Solution:
def minCostToMoveChips(self, position: List[int]) -> int:
odd=even=0
for p in position:
if p&1>0:
odd+=1
else:
even+=1
return min(even,odd)
三、 1029. 两地调度
链接: 1029. 两地调度
1. 题目描述
公司计划面试 2n 人。给你一个数组 costs ,其中 costs[i] = [aCosti, bCosti] 。第 i 人飞往 a 市的费用为 aCosti ,飞往 b 市的费用为 bCosti 。
返回将每个人都飞到 a 、b 中某座城市的最低费用,要求每个城市都有 n 人抵达。
2. 思路分析
每个人不是去a就是去b,不存在不去的情况,
因此找出最该去a的n个人,剩下的去b即可 。
最该去a的人,就是去a比去b省钱最多的人 。
3. 代码实现
class Solution:
def twoCitySchedCost(self, costs: List[List[int]]) -> int:
# 每个人不是去a就是去b,不存在不去的情况,
# 因此找出最该去a的n个人,剩下的去b即可
costs.sort(key=lambda x:x[0]-x[1])
n = len(costs)//2
s = 0
for i in range(n):
s += costs[i][0]
for i in range(n,2*n):
s += costs[i][1]
return s
四、 面试题 10.11. 峰与谷
链接: 面试题 10.11. 峰与谷
1. 题目描述
在一个整数数组中,“峰”是大于或等于相邻整数的元素,相应地,“谷”是小于或等于相邻整数的元素。例如,在数组{5, 8, 4, 2, 3, 4, 6}中,{8, 6}是峰, {5, 2}是谷。现在给定一个整数数组,将该数组按峰与谷的交替顺序排序。
示例:
输入: [5, 3, 1, 2, 3]
输出: [5, 1, 3, 2, 3]
提示:
nums.length <= 10000
2. 思路分析
就是让数组大小交错:每个数两边的数同时大于等于他,或小于等于他。
- 我一开始是按排序思路做的 O(nlog2n)
- 实际上可以O(n),对于每个数,和前边一个数操作来保持它的正确性:如果他是峰,如果它比前边小则交换;如果是谷,比前边大则交换。
- 下面讨论这个方法的正确性:
- 对于i,j,k三个数,j是谷,操作j时,使i>=j,操作k时,k应是峰,如果k小于j,那么k也必然小于i,交换后,新的j依然是小于i,不影响前边的处理。
- 对峰来说亦然。
3. 代码实现
class Solution:
def wiggleSort(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# 题意要求重新排列数组,使每个数两边的数一定同时比它大或同时比它小。
# 1.那么可以把数组排序,中间切一刀,左右两边部分插空即可
# 这样可以使:对于原来在左边的数,两边数都比它大;右边的数依然
# 细节:先放右边的数,这样对于奇数长度的数组可以直接放最后。
# 2.O(n)思路:
# 对每个位置判断是峰还是谷(自己定),然后判断是否和前一个交换
for i in range(1,len(nums)):
if i&1>0:
if nums[i]<nums[i-1]:
nums[i],nums[i-1] = nums[i-1],nums[i]
else:
if nums[i]>nums[i-1]:
nums[i],nums[i-1] = nums[i-1],nums[i]
五、附每日一题 929. 独特的电子邮件地址:
链接: 929. 独特的电子邮件地址
1. 题目描述
每个 有效电子邮件地址 都由一个 本地名 和一个 域名 组成,以 ‘@’ 符号分隔。除小写字母之外,电子邮件地址还可以含有一个或多个 ‘.’ 或 ‘+’ 。
例如,在 alice@leetcode.com中, alice 是 本地名 ,而 leetcode.com 是 域名 。
如果在电子邮件地址的 本地名 部分中的某些字符之间添加句点(‘.’),则发往那里的邮件将会转发到本地名中没有点的同一地址。请注意,此规则 不适用于域名 。
例如,"alice.z@leetcode.com” 和 “alicez@leetcode.com” 会转发到同一电子邮件地址。
如果在 本地名 中添加加号(‘+’),则会忽略第一个加号后面的所有内容。这允许过滤某些电子邮件。同样,此规则 不适用于域名 。
例如 m.y+name@email.com 将转发到 my@email.com。
可以同时使用这两个规则。
给你一个字符串数组 emails,我们会向每个 emails[i] 发送一封电子邮件。返回实际收到邮件的不同地址数目。
2. 思路分析
简单的字符串模拟题,把每个串以最小表示,然后去重计数。
3. 代码实现
class Solution:
def numUniqueEmails(self, emails: List[str]) -> int:
# 模拟
def trans(email):
left,right = email.split('@')
return f"{left.split('+')[0].replace('.','')}@{right}"
return len({trans(email) for email in emails})
六、另附五月四日集训第四题 1400. 构造 K 个回文字符串:
1. 题目描述
给你一个字符串 s 和一个整数 k 。请你用 s 字符串中 所有字符 构造 k 个非空 回文串 。
如果你可以用 s 中所有字符构造 k 个回文字符串,那么请你返回 True ,否则返回 False 。
2. 思路分析
实际上是问,用s所有字符能构造多少个非空回文串。
- 我们知道回文串的定义是左右对称,即s==reversed(s)。
- 单个字符也算回文串。
- 计数一下s中每个字符的数量,成对的字符可以拿出来单算一个串+1,也可依附到别的串里+0,也可拆成2个+1;
- 单个蹦的不能互相组合,所以至少有odd个串。
- 最多n个串(每个字符单成一个)
- 每个成对可以+0,+1,+2,因此可以逐步递增到n。
- 如果k没限制0的话,还需特判一下0。
3. 代码实现
class Solution:
def canConstruct(self, s: str, k: int) -> bool:
return len([1 for v in Counter(s).values() if v &1 ==1]) <= k <= len(s)
# 对每个字符计数,两个一样(pair个)的可以单独成串+1,也可以蹭别的串+0,也可每个一串+2
# 单独的,lonely个,必须自己成串 +1
# 因此最少成even个,最多成even+pair*2个
# 最多实际是n