LeetCode 870. 优势洗牌(根据数值对索引排序)/ 856. 括号的分数(栈) / 801. 使序列递增的最小交换次数(动态规划)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 870. 优势洗牌(根据数值对索引排序)/ 856. 括号的分数(栈) / 801. 使序列递增的最小交换次数(动态规划)相关的知识,希望对你有一定的参考价值。

870. 优势洗牌

2022.10.8 国庆回来,先巩固代码

题目描述

给定两个大小相等的数组 nums1 和 nums2,nums1 相对于 nums 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。

返回 nums1 的任意排列,使其相对于 nums2 的优势最大化。

示例 1:

输入:nums1 = [2,7,11,15], nums2 = [1,10,4,11]
输出:[2,11,7,15]

示例 2:

输入:nums1 = [12,24,8,32], nums2 = [13,25,32,11]
输出:[24,32,8,12]

提示:

1 <= nums1.length <= 10^5
nums2.length == nums1.length
0 <= nums1[i], nums2[i] <= 10^9

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/advantage-shuffle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

超时了
想到要超时了,但是一下想不出怎么才能良好的改进,因为可能出现重复的数字,所以所用的字典还不能直接用num2中的数字来直接作为键,或者说需要将值变成一个列表,但是变成列表也需要
或者说需要有一个字典来记录num2排序后对应的数字顺序

class Solution:
    def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 其实就是在nums1 中找比nums2中数字刚好大一点的数字,这个可以用二分
        # 但是找到以后还需要删除才能找下一个,这样就比较麻烦
        # 所以还是将两个数据排序,然后对应比较直接
        # 将可以对应的数字放到一个字典中,剩下的数字放在一个列表中
        # 然后遍历nums2,按照字典对应关系填充nums1,找不到的就从列表中拿数字
        
        n1 = nums1.copy()
        n2 = nums2.copy()
        n1.sort()
        n2.sort()
        d, lst = dict(), []

        i, j = 0, 0
        while i < len(n1) and j < len(n2):
            # 如果大于,那么放在字典里
            if n1[i] > n2[j]:
                d[j] = n1[i]
                i += 1
                j += 1
            # 如果小于等于,那么放在列表里
            else:
                lst.append(n1[i])
                i += 1
        
        idx = 0
        for i, n in enumerate(nums2):
            for j in d:
                if n == n2[j]:
                    nums1[i] = d[j]
                    d.pop(j)
                    break
            else:
                nums1[i] = lst[idx]
                idx += 1
        return nums1

还是直接二分吧,但是已经找过的数字怎么处理呢?
放在set集合中,然后下一次再找到这个数字就跳过,然后向后取一个没有使用过的
试一试

二分过了

class Solution:
    def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 其实就是在nums1 中找比nums2中数字刚好大一点的数字,这个可以用二分
        # 但是找到以后还需要删除才能找下一个,这样就比较麻烦
        # 所以还是将两个数据排序,然后对应比较直接
        # 将可以对应的数字放到一个字典中,剩下的数字放在一个列表中
        # 然后遍历nums2,按照字典对应关系填充nums1,找不到的就从列表中拿数字
        
        n1 = nums1.copy()
        #n2 = nums2.copy()
        n1.sort()
        #n2.sort()
        s = set()
        l = len(nums1)
        res = [-1] * l

        for i, n in enumerate(nums2):
            left = 0
            right = l - 1
            while left < right:
                mid = (right - left) // 2 + left
                # 如果这个数大于n
                if n1[mid] > n:
                    right = mid
                else:
                    left = mid + 1
            if n1[left] <= n:
                continue
            while left in s:
                left += 1
            if left >= l:
                continue
            s.add(left)
            res[i] = n1[left]
        #print(res)
        idx = 0
        for i, n in enumerate(n1):
            while idx < l and res[idx] != -1:
                idx += 1
            if idx == l:
                break
            if i not in s:
                res[idx] = n
        return res        

官解中直接对索引排序的代码,很巧妙,这个需要学会

class Solution:
    def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:
        n = len(nums1)
        idx1, idx2 = list(range(n)), list(range(n))
        # 直接对索引进行排序
        idx1.sort(key=lambda x: nums1[x])
        idx2.sort(key=lambda x: nums2[x])

        ans = [0] * n
        left, right = 0, n - 1
        for i in range(n):
            # 如果能找到比2中数字大的,那么就填充从左到右填充ans
            if nums1[idx1[i]] > nums2[idx2[left]]:
                ans[idx2[left]] = nums1[idx1[i]]
                left += 1
            else:
                # 当前数字没有2中数字大,那么当前数字多余,所以放在最后
                ans[idx2[right]] = nums1[idx1[i]]
                right -= 1
        
        return ans

856. 括号的分数

2022.10.9 每日一题

题目描述

给定一个平衡括号字符串 S,按下述规则计算该字符串的分数:

() 得 1 分。
AB 得 A + B 分,其中 A 和 B 是平衡括号字符串。
(A) 得 2 * A 分,其中 A 是平衡括号字符串。

示例 1:

输入: “()”
输出: 1

示例 2:

输入: “(())”
输出: 2

示例 3:

输入: “()()”
输出: 2

示例 4:

输入: “(()(()))”
输出: 6

提示:

S 是平衡括号字符串,且只含有 ( 和 ) 。
2 <= S.length <= 50

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/score-of-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution:
    def scoreOfParentheses(self, s: str) -> int:
        # 还是那种栈处理括号的题吧
        # 遇到左括号入栈,遇到右括号出栈,匹配
        # 然后进行计算,计算结果放到栈中,如果前面有数字就做加法
        # 如果右括号前面是数字,那么就做乘法

        stack = []
        for ss in s:
            if ss == '(':
                stack.append(ss)
            else:
                # 如果前面一个是数字,那么记录该数字
                # 不可能出现连续两个数字
                num = 0
                if stack[-1] != '(':
                    num = stack.pop(-1)
                    # 弹出括号
                    stack.pop(-1)
                    num = num * 2
                    if len(stack) > 0 and stack[-1] != '(':
                        num += stack.pop(-1)
                    stack.append(num)
                else:
                    # 如果是括号,那么就等于1
                    # 先将括号弹出
                    stack.pop(-1)
                    num = 1
                    if len(stack) > 0 and stack[-1] != '(':
                        num += stack.pop(-1)
                    stack.append(num)
        return stack.pop(-1)

用数字代替符号

class Solution:
    def scoreOfParentheses(self, s: str) -> int:
        # 直接用数字代替
        # 遇到左括号,是0,遇到右边括号,如果前一个是0,那么就是1
        # 如果前一个不是0,那么就乘以2加上前面的,
        # 其实和之前思路一样,就是写法更简单了

        # 这里为了能使两种情况合并为一种,在开始加了0
        stack = [0]
        for c in s:
            if c == '(':
                stack.append(0)
            else:
                top = stack.pop()
                stack.append(stack.pop() + max(top * 2, 1))
        return stack.pop()

直接看()相邻能形成配对括号的层数

class Solution:
    def scoreOfParentheses(self, s: str) -> int:
        # 第三种,其实就是看()这种括号的层数,有一层就多乘以2
        res = 0
        level = 0
        for i,c in enumerate(s):
            level += 1 if c == '(' else -1
            if c == ')' and s[i - 1] == '(':
                res += 1 << level
        return res

801. 使序列递增的最小交换次数

2022.10.10 每日一题

题目描述

我们有两个长度相等且不为空的整型数组 nums1 和 nums2 。在一次操作中,我们可以交换 nums1[i] 和 nums2[i]的元素。

  • 例如,如果 nums1 = [1,2,3,8] , nums2 =[5,6,7,4] ,你可以交换 i = 3 处的元素,得到 nums1 =[1,2,3,4] 和 nums2 =[5,6,7,8] 。
    返回 使 nums1 和 nums2 严格递增 所需操作的最小次数 。

数组 arr 严格递增 且 arr[0] < arr[1] < arr[2] < … < arr[arr.length - 1] 。

注意:

  • 用例保证可以实现操作。

示例 1:

输入: nums1 = [1,3,5,4], nums2 = [1,2,3,7]
输出: 1
解释:
交换 A[3] 和 B[3] 后,两个数组如下:
A = [1, 3, 5, 7] , B = [1, 2, 3, 4]
两个数组均为严格递增的。

示例 2:

输入: nums1 = [0,3,5,8,9], nums2 = [2,1,4,6,9]
输出: 1

提示:

2 <= nums1.length <= 10^5
nums2.length == nums1.length
0 <= nums1[i], nums2[i] <= 2 * 10^5

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-swaps-to-make-sequences-increasing
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

好久不做dp了,有点难搞

class Solution:
    def minSwap(self, nums1: List[int], nums2: List[int]) -> int:
        # 看数据范围,最多是个nlogn的复杂度
        # 而且看到了顺序,所以二分可能会用到
        # 然后呢,看了下例子,好像是看到不满足顺序的,就需要进行调整
        # 但是怎么调整,换哪个呢
        # 为了能够更好的接上后面的数字,肯定是使递增序列越小越好
        # 动态规划
        # dp[i][0] 表示i位置不交换使得递增的最少次数,dp[i][1]表示交换使得递增的最少次数
        # 如果当前位置符合交换的条件1,即a2>a1 && b2>b1,则可以都交换或者都不交换
        # 如果当前位置符合交换的条件2,即a2>b1 && b2>a1,则可以交换1次
        # 如果都满足,取较小值

        l = len(nums1)
        dp = [[l+1, l+1] for _ in range(l)]
        
        dp[0] = [0, 1]
        for i in range(1, l):
            if nums1[i] > nums1[i - 1] and nums2[i] > nums2[i - 1]:
                # 上一次不交换这次也不换
                dp[i][0] = dp[i - 1][0]
                # 上一次交换这次也得交换
                dp[i][1] = dp[i - 1][1] + 1
            if nums1[i] > nums2[i - 1] and nums2[i] > nums1[i - 1]:
                # 对于这次不交换的情况,如果上次没交换,说明肯定a2>a1,b2>b1所以这次也不换
                # 如果上次交换了,那么这次就不用换了
                dp[i][0] = min(dp[i][0], dp[i - 1][1])
                # 对于本次交换了的情况,如果上次交换了,那么本次就不用交换了,否则需要交换
                dp[i][1] = min(dp[i][1], dp[i - 1][0] + 1)            
        return min(dp[l - 1][0], dp[l - 1][1])

以上是关于LeetCode 870. 优势洗牌(根据数值对索引排序)/ 856. 括号的分数(栈) / 801. 使序列递增的最小交换次数(动态规划)的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode(870)-优势洗牌

LeetCode 870. 优势洗牌(田忌赛马问题)

LeetCode 870 优势洗牌[自定义排序] HERODING的LeetCode之路

LeetCode 870 优势洗牌[自定义排序] HERODING的LeetCode之路

力扣 每日一题 870. 优势洗牌难度:中等(贪心+双指针)

力扣 每日一题 870. 优势洗牌难度:中等,rating: 1648(贪心+双指针)