[英雄星球六月集训LeetCode解题日报] 第四日 贪心

本文介绍了如何使用贪心策略解决LeetCode中的五个问题,包括分割平衡字符串、玩筹码最小成本、两地调度、峰谷排序以及独特电子邮件地址去重。通过实例解析和代码实现,深入浅出地展示了贪心算法在这些问题中的应用。
摘要由CSDN通过智能技术生成

一、 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. 思路分析

就是让数组大小交错:每个数两边的数同时大于等于他,或小于等于他。

  1. 我一开始是按排序思路做的 O(nlog2n)
  2. 实际上可以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 个回文字符串:

链接: 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

七、参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值