Python解题:Leetcode周赛(303)
题目一:第一个出现两次的字母
给你一个由小写英文字母组成的字符串
s
,请你找出并返回第一个出现 两次 的字母。注意:
- 如果
a
的 第二次 出现比b
的 第二次 出现在字符串中的位置更靠前,则认为字母a
在字母b
之前出现两次。s
包含至少一个出现两次的字母。
难度:简单
示例 1:
输入:s = "abccbaacz" 输出:"c" 解释: 字母 'a' 在下标 0 、5 和 6 处出现。 字母 'b' 在下标 1 和 4 处出现。 字母 'c' 在下标 2 、3 和 7 处出现。 字母 'z' 在下标 8 处出现。 字母 'c' 是第一个出现两次的字母,因为在所有字母中,'c' 第二次出现的下标是最小的。
示例 2:
输入:s = "abcdd" 输出:"d" 解释: 只有字母 'd' 出现两次,所以返回 'd' 。
分析
这道送分题,可以有N种解法,以至于时间都花在考虑用哪种解法更优了。其实不同解法也只是有细微的差别,无非就是从左至右找出字符串里最先出现的重复的字母。可以比较每个字母在字符串的下标,也可以依次遍历字符串,找到第一个出现两次的字母即返回,还可以对字符串进行切片等等,道理和遍历差不多。这里只写一种就好,因为就本题而言,差别真的不大。
代码实现
s = input()
for i in range(len(s)):if s[i] in s[:i]: print(s[i])break
题目二:相等行列对
给你一个下标从 0 开始、大小为
n x n
的整数矩阵grid
,返回满足Ri
行和Cj
列相等的行列对(Ri, Cj)
的数目。如果行和列以相同的顺序包含相同的元素(即相等的数组),则认为二者是相等的。
难度:中等
示例 1:
输入:grid = [[3,2,1],[1,7,6],[2,7,7]] 输出:1 解释:存在一对相等行列对: - (第 2 行,第 1 列):[2,7,7]
示例 2:
输入:grid = [[3,1,2,2],[1,4,4,5],[2,4,2,2],[2,4,2,2]] 输出:3 解释:存在三对相等行列对: - (第 0 行,第 0 列):[3,1,2,2] - (第 2 行, 第 2 列):[2,4,2,2] - (第 3 行, 第 2 列):[2,4,2,2]
分析
本题要找出有多少组相等的行列对,由于python自己的列表没有矩阵的结构,无法像numpy那样把矩阵转置,得到由列元素组成的二维列表,所以需要先手动把列表进行转置,组成列列表。然后通过遍历原矩阵(行列表),行元素与列元素相同,也就意味着同样的行元素也出现在转置的列表中即可组成符合要求的行列对,于是将每一行元素在转置的列表中出现的次数相加即可。
第二种解法正好反过来,先求出原矩阵中每个行元素出现的次数,然后在转置列表中找到每个列元素在原矩阵出现的次数。其实逻辑是相通的,只不过第二种解法不用先生成转置列表,而是可以使用zip生成一个可迭代对象,在遍历的同时生成转置,从而节省时间。
代码实现
解法一:
grid = eval(input())
trans = []
for i in range(len(grid)):trans.append([j[i] for j in grid])
cnt = 0
for i in grid:cnt += trans.count(i)
print(cnt)
解法二:
统计原矩阵中相同行元素出现的次数,最好使用字典结构,方便进行查找,而使用Counter对象会更加节省时间。
遍历zip对象的时候,我们只需要将每一个列元素在原矩阵中出现的次数累加即可,所以并不需要生成列表,使用推导式即可完成。
from collections import Counter
grid = eval(input())
row = Counter(tuple(i) for i in grid)
res = sum(row[i] for i in zip(*grid))
print(res)
题目三:设计食物评分系统
设计一个支持下述操作的食物评分系统:
- 修改 系统中列出的某种食物的评分。
- 返回系统中某一类烹饪方式下评分最高的食物。
实现
FoodRatings
类:
FoodRatings(String[] foods, String[] cuisines, int[] ratings)
初始化系统。食物由foods
、cuisines
和ratings
描述,长度均为n
。
foods[i]
是第i
种食物的名字。cuisines[i]
是第i
种食物的烹饪方式。ratings[i]
是第i
种食物的最初评分。void changeRating(String food, int newRating)
修改名字为food
的食物的评分。String highestRated(String cuisine)
返回指定烹饪方式cuisine
下评分最高的食物的名字。如果存在并列,返回 字典序较小的名字。注意,字符串
x
的字典序比字符串y
更小的前提是:x
在字典中出现的位置在y
之前,也就是说,要么x
是y
的前缀,或者在满足x[i] != y[i]
的第一个位置i
处,x[i]
在字母表中出现的位置在y[i]
之前。
难度:中等
示例:
输入 ["FoodRatings", "highestRated", "highestRated", "changeRating", "highestRated", "changeRating", "highestRated"] [[["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"], ["korean", "japanese", "japanese", "greek", "japanese", "korean"], [9, 12, 8, 15, 14, 7]], ["korean"], ["japanese"], ["sushi", 16], ["japanese"], ["ramen", 16], ["japanese"]] 输出 [null, "kimchi", "ramen", null, "sushi", null, "ramen"]解释 foodRatings = FoodRatings(["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"], ["korean", "japanese", "japanese", "greek", "japanese", "korean"], [9, 12, 8, 15, 14, 7]); foodRatings.highestRated("korean") # 返回 "kimchi"# "kimchi" 是分数最高的韩式料理,评分为 9 。 foodRatings.highestRated("japanese") # 返回 "ramen"# "ramen" 是分数最高的日式料理,评分为 14 。 foodRatings.changeRating("sushi", 16) # "sushi" 现在评分变更为 16 。 foodRatings.highestRated("japanese") # 返回 "sushi"# "sushi" 是分数最高的日式料理,评分为 16 。 foodRatings.changeRating("ramen", 16) # "ramen" 现在评分变更为 16 。 foodRatings.highestRated("japanese") # 返回 "ramen"# "sushi" 和 "ramen" 的评分都是 16 。# 但是,"ramen" 的字典序比 "sushi" 更小。
分析
构造题。和上一篇双周赛的构造题类似,这题唯一的难度在于如何快速形成有序的数据结构。
因为题目给出了三个字段:cuisine、food、rating,所以为了方便互相引用,至少需要两个字典。字典1将cuisine作为键,用来记录该菜系下有什么food,以及food的评分。而字典2用来记录food所属的菜系以及评分。当然也可以使用三个字典,字典3用来记录同样的评分下面有哪些food及其菜系。但是因为最后的查询函数是找出同一菜系cuisine下评分最高的food,所以字典1必不可少,字典2用来更新评分,而字典3其实并不需要。
和上篇文章类似,可以使用有序列表或堆排序来构建字典1,这样,在放入food和rating的时候,就可以自动排序将符合查询要求的food放在列表首位或堆顶。需要注意的是,我们要维护两个排序,一个是food名称的字典序,另一个是rating排序,所以要把(rating, food)这样的组合里rating在前,food在后,简单说就是最后排序的放在前面,这样有序列表或堆会先按照food再按照rating进行排序。另外要注意的是,有序列表或堆排序默认都是小顶堆,所以food的字典序其实也是ASCII码小的在前面,但是我们是要找出rating最大的,所以在保存的时候,要将rating取反,使最大的变成最小的,这样就自动把raiting最大的放在堆顶了。
字典2的结构随意,只要能够根据food找到其对应的cuisine和rating即可。
代码实现
解法一:使用有序列表 SortedList
class FoodRatings:def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):from sortedcontainers import SortedListself.cuisine = {}self.food = {}for f, c, r in zip(foods, cuisines, ratings):self.food[f] = [c, r]self.cuisine.setdefault(c,SortedList()).add((-r, f))def changeRating(self, food: str, newRating: int) -> None:c, r = self.food[food]self.food[food][1] = newRatingself.cuisine[c].remove((-r, food))self.cuisine[c].add((-newRating, food))def highestRated(self, cuisine: str) -> str:return self.cuisine[cuisine][0][1]
解法二:使用堆排序 heapq
上篇文章讲过,因为heapq没有办法对堆顶之外的数据进行删除和修改,所以需要在查询的时候依次取出堆顶,与字典2进行比较,如果不同,则说明food的rating被修改过,则自动抛弃堆顶,继续取出下一个。如果相同,则还要将取出的数据重新入堆,以方便下次查找。
import heapqclass FoodRatings:def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):self.cuisine = {}self.food = {}for f, c, r in zip(foods, cuisines, ratings):self.food[f] = [c, r]heapq.heappush(self.cuisine.setdefault(c,list()),(-r, f))def changeRating(self, food: str, newRating: int) -> None:heapq.heappush(self.cuisine[self.food[food][0]],(-newRating, food))self.food[food][1] = newRatingdef highestRated(self, cuisine: str) -> str:while True:r,f = heapq.heappop(self.cuisine[cuisine])if self.food[f][1] == -r:heapq.heappush(self.cuisine[cuisine],(r, f))return f
题目四:优质数对的数目
给你一个下标从 0 开始的正整数数组
nums
和一个正整数k
。如果满足下述条件,则数对
(num1, num2)
是 优质数对 :
num1
和num2
都 在数组nums
中存在。num1 OR num2
和num1 AND num2
的二进制表示中值为 1 的位数之和大于等于k
,其中OR
是按位 或 操作,而AND
是按位 与操作。返回 不同 优质数对的数目。
如果
a != c
或者b != d
,则认为(a, b)
和(c, d)
是不同的两个数对。例如,(1, 2)
和(2, 1)
不同。注意:如果
num1
在数组中至少出现 一次 ,则满足num1 == num2
的数对(num1, num2)
也可以是优质数对。
难度:困难
示例 1:
输入:nums = [1,2,3,1], k = 3 输出:5 解释:有如下几个优质数对: - (3, 3):(3 AND 3) 和 (3 OR 3) 的二进制表示都等于 (11) 。值为 1 的位数和等于 2 + 2 = 4 ,大于等于 k = 3 。 - (2, 3) 和 (3, 2): (2 AND 3) 的二进制表示等于 (10) ,(2 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。 - (1, 3) 和 (3, 1): (1 AND 3) 的二进制表示等于 (01) ,(1 OR 3) 的二进制表示等于 (11) 。值为 1 的位数和等于 1 + 2 = 3 。 所以优质数对的数目是 5 。
示例 2:
输入:nums = [5,1,1], k = 10 输出:0 解释:该数组中不存在优质数对。
分析
题目说了许多废话,其实“阅读理解”后的归纳就是:从整数数组nums中先后取出两个数作为一组数对,同一个数字可以重复选取,比如 (1,1),而顺序不同的数对视做不同数对,比如 (1,2) 和 (2,1) 就是不同的数对,那么优质数对有多少组?
这题主要的难点在于如何理解“优质数对”的定义,如果对于数对(a,b),两个数字或操作结果的二进制1的个数表示为 f(a|b),与操作表示为 f(a&b),满足“优质数对”条件的函数表示就是
f(a|b) + f(a&b) >= k
通过数学知识,或者通过观察,可以得出:f(a|b) + f(a&b) = f(a) + f(b) 。也就是说,两个数或操作和与操作结果二进制1的位数之和等于二者自身二进制1的位数之和。
这一步很关键,因为它大大优化了我们的查找效率,不然我们需要使用穷举搜索,把从数组中取出两个数的所有可能都找出来,然后依次使用条件 f(a|b) + f(a&b) >= k 来进行判断。算法的基础来自于数据结构。如果没有数据结构,或者是未知的数据结构,就像是在乱序的数字里找到某个特定的数字,只能穷举——虽然穷举也是一种算法。而如果我们知道 f(a|b) + f(a&b) 等价于 f(a) + f(b),那就可以把数组中每个数字对应的二进制1的位数先求出来,根据位数将数字进行排序,形成一个有序的数据结构。只要通过指针遍历,就可以较简单地通过判断 f(a) + f(b) >= k 来将所有结果找出。
因为题目只要求出个数,不需要知道是哪些数对,所以我们可以再进一步,将数字转化成二进制1的位数,形成一个列表,然后再对其进行排序。
第一种解法是两个指针,对该列表从两端开始遍历,外指针从前往后,内指针从后往前。先固定外指针,移动内指针,依次检查列表里两个数字的和是否大于等于k,如果是,则内指针继续向前移,一直到小于 k 或者 pos=0(内指针指向第一个元素,结束遍历)的时候,就表示对于外指针当前指向的数字而言,内指针往后的所有数字都是满足条件的,而这样的长度是列表的长度减去pos。再定义一个累加器,加上这个长度,然后将外指针向后移动一位,检查第二个数字。注意,这时候内指针不需要重置,可以由现在的位置继续前移(如果不为0的话),因为外指针指向的数字比上一个数字大,所以与内指针之后的数字必然满足条件。当外指针指向最后一个数字,累加结束,得到的值就是返回的结果。
第二种解法更加高效,可以根据数组中数字的二进制1的位数作为键,位数相同的数字的个数作为值,组成一个字典,然后同样使用内外两个指针遍历字典,将满足条件的位数所对应的数字出现次数相乘,就是这些数字所能组成“优质数对”的个数,同样累加即可得到结果。而因为使用字典的哈希结构,这一过程将比列表更加省时。
代码实现
解法一:
nums = eval(input())
k = int(input())
temp = sorted([bin(n).count('1') for n in set(nums)])
l = len(temp)
pos = l
res = 0
for i in range(l):while pos > 0 and temp[pos-1] + temp[i] >= k:pos -= 1res += l-pos
print(res)
解法二:
nums = eval(input())
k = int(input())
from collections import Counter
cnt = Counter(bin(n).count('1') for n in set(nums))
res = 0
for a, cnta in cnt.items():for b, cntb in cnt.items():if a+b >= k: res += cnta*cntb
print(res)
Python解题:Leetcode周赛(303)相关推荐
- Python解题 - CSDN周赛第25期 - 水池注水
本期四道题都是在每日一练出现过多次的老题,而且后两道题的思维难度颇大,尤其最后一题-水池注水,如果没有提前准备过,问哥是万万不可能在两个小时内做出来的.所以,拿到这个名次,问哥心里其实很虚.提交之后发 ...
- Python解题 - CSDN周赛第36期
本期有点难度,系统也没有多少bug,算是最近 N 期周赛里质量较高的一期比赛了. 第一题:查找点在自然区间的坐标 定义:实数轴上的一个区间由左右两个端点,假设区间是左闭右开的,例如区间`[0,1)`. ...
- Python解题 - CSDN周赛第29期 - 争抢糖豆
本期问哥是志在必得,这本算法书我已经觊觎许久,而之前两次因为种种原因未能如愿.因此,问哥这几天花了不少时间,把所有之前在每日一练做过的题目重新梳理了一遍.苦心人,天不负,感谢官方大大! 第一题:订班服 ...
- Python解题 - CSDN周赛第32期 - 运输石油(三维背包)
上期周赛因为最后一题出现bug,再加上都是经典的模板题,问哥就懒得写题解了. 本期也是有两道考过的题目,不过最后一题因为考到了背包问题的特殊类型,还是值得拿出来记个笔记. 第一题:传奇霸业 传奇霸业, ...
- Python解题 - CSDN周赛第23期 - 树形背包与优化
以问哥目前的水平来看,本期的四道题的整体难度还是中等偏上的,而且从结果上来看, 也达到了竞赛的标准(只有三名选手拿到满分).也许在某些大佬看来还是太简单了,毕竟都是模板题,直接套模板就能过,但对于这些 ...
- Python解题 - CSDN周赛第40期
上期问哥没参加,但从赛后大家的反馈来看,又出现了数据上的bug,使用 python 的朋友会遇到第二个用例的柱子高度数组长度不够,200根柱子,只有179个数据,这让人怎么玩?但是用C++的选手就没有 ...
- Python描述 LeetCode 875. 爱吃香蕉的珂珂
Python描述 LeetCode 875. 爱吃香蕉的珂珂 大家好,我是亓官劼(qí guān jié ),在[亓官劼]公众号.CSDN.GitHub.B站等平台分享一些技术博文,主要包括前端开 ...
- C++Python描述 LeetCode 1109. 航班预订统计
C++&Python描述 LeetCode 1109. 航班预订统计 大家好,我是亓官劼(qí guān jié ),在公众号.CSDN.GitHub.B站.华为开发者论坛等平台分享一些技 ...
- Python描述 LeetCode 732. 我的日程安排表 III
Python描述 LeetCode 732. 我的日程安排表 III 大家好,我是亓官劼(qí guān jié ),在[亓官劼]公众号.CSDN.GitHub.B站等平台分享一些技术博文,主要包 ...
最新文章
- CVPR 2020最佳学生论文分享回顾:通过二叉空间分割(BSP)生成紧凑3D网格
- 02 数据类型 (向量 数据框 矩阵和列表
- django写项目的详细步骤
- php sql 条件拼组_ThinkPHP框架SQL操作链式写法原理(浅显易懂)
- JSON与JavaScript的转换
- Delphi中DLL或Package窗体载入与显示
- iOS中使用UIActivityViewController系统分享到微信QQ微博包含文字图片URL
- 合伙人和创始人的区别
- smartview for office的安装即配置
- 探究Ptcms小说采集规则
- Unity shader 空气扭曲效果
- CSS gird布局
- Kubernetes(K8S)入门到运维 ( 六) Helm与功能组件及证书年限
- 20 个关于程序员的笑话,看懂了,你就不会笑了,也不会羡慕他们工资高了!...
- 棋牌游戏服务端架构(总)
- 超详细的Asp.net使用SSL双向认证,一篇就够了
- 【Java学习之代码学习】 Prog25_求闰年个数的问题
- 谈谈实习第一天的感受
- Java8集合之HashMap的hash计算、扩容等问题
- 领域搜索算法java_使用JAVA实现算法——禁忌搜索算法解决TSP问题