秒杀算法面试题的第3个理由——力扣周赛240期解析

比赛地址:
https://leetcode-cn.com/contest/weekly-contest-240
欢迎关注我的公众号,了解算法岗面经、深圳教师招聘备考指南、程序猿的技术之旅与浪漫生活~

先祝大家母亲节快乐!下面看题…

Question 1

题目

输入一个二维整数数组 logs ,其中
l o g s [ i ] = [ b i r t h i , d e a t h i ] logs[i] = [birth_i, death_i] logs[i]=[birthi,deathi]表示第i个人的出生和死亡年份。年份x的人口定义为这一年期间活着的人的数目。第i个人被计入年份x的人口需要满足: x ∈ [ b i r t h i , d e a t h i − 1 ] x\in [birthi, deathi-1] x[birthi,deathi1]内。
返回 人口最多且最早的年份。

输入:logs = [[1950,1961],[1960,1971],[1970,1981]]
输出:1960
解释: 
人口最多为 2 ,分别出现在 1960 和 1970 。
其中最早年份是 1960 。

解析

使用长度为100的数组,分别保存每一年份的人数,最后使用index()返回最大值第一次出现的年份。

答案

class Solution:
    def maximumPopulation(self, logs: List[List[int]]) -> int:
        import numpy as np
        rst = np.array([0] * 100)
        for l, r in logs:
            l, r = l - 1950, r - 1950
            rst[l: r] += 1
        return rst.tolist().index(max(rst)) + 1950

Question 2

题目

给定2个非递增的整数数组 nums1和 nums2,数组下标均 从 0 开始 计数。下标对 (i, j)同时满足 i ≤ j i\leq j ij n u m s 1 [ i ] ≤ n u m s 2 [ j ] nums1[i]\leq nums2[j] nums1[i]nums2[j],则称之为有效下标对,该下标对的距离为 j − i j-i ji。返回所有有效下标对(i, j)中的 最大距离,如果不存在有效下标对,返回0。

输入:nums1 = [55,30,5,4,2], nums2 = [100,20,10,10,5]
输出:2
解释:有效下标对是 (0,0), (2,2), (2,3), (2,4), (3,3), (3,4) 和 (4,4) 。
最大距离是 2 ,对应下标对 (2,4) 。

解析

若想得到最大的 j − i j-i ji,则在i固定时尽可能让j向后推

nums1 = [55,30,5,4,2]

nums2 = [70,65,45,20,5]

若i=0,则j最大为1

若i=1,则j最大为2

若i=2,则j最大为4

通过观察容易发现,每当i向后推进1位,由于数组非递增, n u m s 1 [ i ] ≥ n u m s 1 [ i + 1 ] nums1[i]\geq nums1[i+1] nums1[i]nums1[i+1],因此j可以继续向后推进,即 j i ≥ j i + 1 j_i \geq j_{i+1} jiji+1,在遍历i时,j只需要一直向后推进,因此可以将 i , j i,j i,j均放在数组开头,每次将i向后推进一位,并更新j的值,数组中的每个元素只会被遍历一次,因此该解法是 O ( n ) O(n) O(n)的。

最后需要注意有可能不存在这样的数对。

答案

class Solution:
    def maxDistance(self, nums1: List[int], nums2: List[int]) -> int:
        i, j, rst = 0, 0, 0
        m = -float('inf')
        l1, l2 = len(nums1), len(nums2)
        # 将i向后推进1
        while i < l1:
            # 对应的将j向后推进
            while j < l2 and nums2[j] >= nums1[i]:
                j += 1
            m = max(m, j - i - 1)
            i += 1
        # m≤0则说明不存在有效下标对
        return max(m, 0)

Question 3

题目

一个数组的最小乘积定义为:这个数组中最小值乘以数组元素之和。给定正整数数组 nums,返回nums任意非空子数组的最小乘积的最大值。答案对 1 0 9 + 7 10^9+7 109+7 取余。子数组为一个数组的连续部分。

示例

输入:nums = [1,2,3,2]
输出:14
解释:最小乘积的最大值由子数组 [2,3,2] (最小值是 2)得到。
2 * (2+3+2) = 2 * 7 = 14 。

解析

  • 为了更快计算每个子数组之和,先计算前缀和。
  • 子数字的最小乘积与最小值(坑)有关,因此需要确定每个"坑"的势力范围,如数组[3, 5, 6, 1, 3, 2, 5]中,任何经过1的子数组均受1影响,而2仅能影响其左边的3与右边的5,再向左则受1的影响,因此需要确定每个数字能影响的范围边界,左边界实际上是每个数字左边第一个它的数的位置,该上述数组每个数字的左边界为[-1, 0, 1, -1, 3, 3, 5],其中-1表示该数字左边无更小元素,故可影响到最左边。相应地,右边界为[3, 3, 3, 7, 5, 7, 7],7表示该数字右边无更小元素,故可影响到最右边。
  • 左边界与右边界可以使用单调栈进行求解,当新加入元素更小时,前面的元素找到了右边界,反之需入栈并等待右边界的出现。
  • 事实上左边界与右边界的求解可以一次性完成。考虑右边界的求解过程:当从左到右加入x时,x左边更大的数不可能为x的左边界(因为x更小),而x左边更小的数则必然被保留在单调栈中,因此单调栈中的信息同样足够计算左边界,在x入栈时的栈顶元素(除了x)即为x的左边界。
  • 需要注意若右边出现与栈顶相等的数x,则x不会被当作右边界,从而影响寻找x的左边界,故可以规定右边界为小于等于的数,而左边界为严格小于的数,相等的几个数中的最右边的数是能够确定最大左右边界的,故这种做法对结果无影响。

答案

class Solution:
    def maxSumMinProduct(self, nums: List[int]) -> int:
        n = len(nums)
        pre_sums = nums[:]
        for i in range(1, n):
            pre_sums[i] += pre_sums[i - 1]
        pre_sums.append(0)

        right_less_poi, left_less_poi = [n] * n, [-1] * n
        stack = []
        for i in range(n):
            while stack and nums[stack[-1]] >= nums[i]:
                right_less_poi[stack.pop()] = i
            if stack:
                left_less_poi[i] = stack[-1]
            stack.append(i)
            
        return max(num * (pre_sums[right - 1] - pre_sums[left]) for left, right, num in zip(left_less_poi, right_less_poi, nums)) % (10 ** 9 + 7)

Question 4

题目

给定有向图 ,它含有n个节点和m条边。节点编号从0到n-1。字符串colors,是小写英文字母,表示图中节点的颜色,二维数组edges,其中edges[j]=[aj, bj]表示从节点aj到节点bj有一条有向边。路径的颜色值是路径中出现次数最多颜色的节点数目。返回图中有效路径里面的最大颜色值。如果图中含有环,返回-1。

示例

输入:colors = "abaca", edges = [[0,1],[0,2],[2,3],[3,4]]
输出:3
解释:路径 0 -> 2 -> 3 -> 4 含有 3 个颜色为 "a" 的节点(上图中的红色节点)。

思路(拓扑排序+动态规划)

  • 我们不需要保存每一条路径,以红色为例,在传递过程中只需要知道以父节点为终点的路径中最多包含多少个红色节点,即可确定以当前节点为终的路径最多包含多少个红色节点,因此只需保留每个节点的每个颜色的最大数量
  • 统计节点的入度,并将入度为0(无父节点)者加入stack
  • 逐个将节点弹出stack,计算以它为终点的路径最多包含多少个各种颜色的节点,并基于此更新其子节点,若子节点无未计算的父节点,则入stack
  • 每个节点在出stack时,其所有父节点必全部计算完成,故只需加上它自己的颜色,即可计算完成
  • 若总出stack的数量为n,则说明没有环,否则有环(环上的节点永远无法入stack,故不会陷入死循环)

答案

class Solution:
    def largestPathValue(self, colors: str, edges: List[List[int]]) -> int:
        n = len(colors)
        colors = [ord(s) - 97 for s in colors]
        graph = [[] for _ in range(n)]
        visited = n
        in_deg = [0] * n
        for x, y in edges:
            in_deg[y] += 1
            graph[x].append(y)
        
        rst = [[0] * 26 for _ in range(n)]
        stack = [i for i in range(n) if not in_deg[i]]
        
        while stack:
            node = stack.pop()
            visited -= 1
            rst[node][colors[node]] += 1
            for nei in graph[node]:
                in_deg[nei] -= 1
                for color in range(26):
                    rst[nei][color] = max(rst[nei][color], rst[node][color])
                if not in_deg[nei]:
                    stack.append(nei)
        
        return -1 if visited else max([max(rst[i]) for i in range(n)])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值