Leetcode 3-2

46. 全排列

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

输入:stones = [31,26,33,21,40]
输出:5

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 100

动态规划(自己写的)

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        n = len(stones)
        target = sum(stones)//2

        dp = [[0 for _ in range(target+1)] for _ in range(n)]

        for j in range(1,target+1):
            if j > stones[0]:
                dp[0][j] = stones[0]

        for i in range(n):
            for j in range(1,target+1):
                if j >= stones[i]:
                    dp[i][j] = max(dp[i-1][j],dp[i-1][j-stones[i]] + stones[i])
                else:
                    dp[i][j] = dp[i-1][j]

        return sum(stones) - 2*dp[-1][-1]

动态规划(优化了空间和初始化的)

from typing import List

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        total = sum(stones)
        target = total // 2
        
        # 使用一维数组进行空间优化
        dp = [0] * (target + 1)
        
        for stone in stones:
            # 逆序遍历以避免覆盖未处理的数据
            for j in range(target, stone - 1, -1):
                dp[j] = max(dp[j], dp[j - stone] + stone)
                
        return total - 2 * dp[target]


1143. 最长公共子序列

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1text2 仅由小写英文字符组成。

动态规划记忆化

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        n = len(text1)
        m = len(text2)

        # cache装饰器:实现记忆化
        @cache
        def dfs(i, j):
            if i < 0 or j < 0:
                return 0
            if text1[i] == text2[j]:
                return dfs(i-1, j-1) + 1
            return max(dfs(i-1, j), dfs(i, j-1))

        return dfs(n-1, m-1)

动态规划法

超出时间限制

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        n = len(text1)
        m = len(text2)

        def dfs(i, j):
            if i < 0 or j < 0:
                return 0
            if text1[i] == text2[j]:
                return dfs(i-1, j-1) + 1
            return max(dfs(i-1, j), dfs(i, j-1))

        return dfs(n-1, m-1)

动态规划(超出内存限制)

from typing import List
import bisect

class Solution:
    def maximumBeauty(self, items: List[List[int]], queries: List[int]) -> List[int]:
        # 按价格升序排列;若价格相同,则按美丽值降序排列
        items.sort(key=lambda x: (x[0], -x[1]))
        
        # 构建前缀最大美丽值映射
        prefix_max_beauty = {}
        max_beauty = 0
        for price, beauty in items:
            if price not in prefix_max_beauty:
                max_beauty = max(max_beauty, beauty)
                prefix_max_beauty[price] = max_beauty
            else:
                # 如果已经有这个价格了,因为排序保证了美丽值递减,所以无需更新
                pass
        
        # 提取所有已记录的价格形成有序列表,用于二分查找
        prices = sorted(prefix_max_beauty.keys())
        
        result = []
        for query in queries:
            # 使用二分查找找到不大于查询价格的最大价格
            idx = bisect.bisect_right(prices, query) - 1
            if idx >= 0:
                result.append(prefix_max_beauty[prices[idx]])
            else:
                result.append(0)
        
        return result

2070. 每一个查询的最大美丽值

给你一个二维整数数组 items ,其中 items[i] = [pricei, beautyi] 分别表示每一个物品的 价格美丽值

同时给你一个下标从 0 开始的整数数组 queries 。对于每个查询 queries[j] ,你想求出价格小于等于 queries[j] 的物品中,最大的美丽值 是多少。如果不存在符合条件的物品,那么查询的结果为 0

请你返回一个长度与 queries 相同的数组 answer,其中 answer[j]是第 j 个查询的答案。

示例 1:

输入:items = [[1,2],[3,2],[2,4],[5,6],[3,5]], queries = [1,2,3,4,5,6]
输出:[2,4,5,5,6,6]
解释:
- queries[0]=1 ,[1,2] 是唯一价格 <= 1 的物品。所以这个查询的答案为 2 。
- queries[1]=2 ,符合条件的物品有 [1,2] 和 [2,4] 。
  它们中的最大美丽值为 4 。
- queries[2]=3 和 queries[3]=4 ,符合条件的物品都为 [1,2] ,[3,2] ,[2,4] 和 [3,5] 。
  它们中的最大美丽值为 5 。
- queries[4]=5 和 queries[5]=6 ,所有物品都符合条件。
  所以,答案为所有物品中的最大美丽值,为 6 。

示例 2:

输入:items = [[1,2],[1,2],[1,3],[1,4]], queries = [1]
输出:[4]
解释:
每个物品的价格均为 1 ,所以我们选择最大美丽值 4 。
注意,多个物品可能有相同的价格和美丽值。

示例 3:

输入:items = [[10,1000]], queries = [5]
输出:[0]
解释:
没有物品的价格小于等于 5 ,所以没有物品可以选择。
因此,查询的结果为 0 。

提示:

  • 1 <= items.length, queries.length <= 105
  • items[i].length == 2
  • 1 <= pricei, beautyi, queries[j] <= 109

动态规划

超出内存限制

class Solution:
    def maximumBeauty(self, items: List[List[int]], queries: List[int]) -> List[int]:
        price = lambda x:items[x][0]
        beauty = lambda x:items[x][1]
        items.sort(key = lambda x:(x[0],x[1]))

        dp = [0 for _ in range(price(-1)+1)]
        # 动态规划遍历items数组得到每个查询的最大美丽值
        
        for i in range(len(items)):
            dp[price(i)] = max(dp[price(i)],dp[price(i)-1],beauty(i))

        # 再遍历一遍构成前缀最大值
        for i in range(1,len(dp)):
            dp[i] = max(dp[i-1],dp[i])

        result = []
        for query in queries:
            if query <= len(dp)-1:
                result.append(dp[query])
            else:
                result.append(dp[-1])

        return result

哈希+二分

159ms 击败75.36%

from typing import List
import bisect

class Solution:
    def maximumBeauty(self, items: List[List[int]], queries: List[int]) -> List[int]:
        # 按价格升序排列;若价格相同,则按美丽值降序排列
        items.sort(key=lambda x: (x[0], -x[1]))
        
        # 构建前缀最大美丽值映射
        prefix_max_beauty = {}
        max_beauty = 0
        for price, beauty in items:
            if price not in prefix_max_beauty:
                max_beauty = max(max_beauty, beauty)
                prefix_max_beauty[price] = max_beauty
            else:
                # 如果已经有这个价格了,因为排序保证了美丽值递减,所以无需更新
                pass
        
        # 提取所有已记录的价格形成有序列表,用于二分查找
        prices = sorted(prefix_max_beauty.keys())
        
        result = []
        for query in queries:
            # 使用二分查找找到不大于查询价格的最大价格
            idx = bisect.bisect_right(prices, query) - 1
            if idx >= 0:
                result.append(prefix_max_beauty[prices[idx]])
            else:
                result.append(0)
        
        return result

2502. 设计内存分配器

给你一个整数 n ,表示下标从 0 开始的内存数组的大小。所有内存单元开始都是空闲的。

请你设计一个具备以下功能的内存分配器:

  1. 分配 一块大小为 size 的连续空闲内存单元并赋 id mID
  2. 释放 给定 id mID 对应的所有内存单元。

注意:

  • 多个块可以被分配到同一个 mID
  • 你必须释放 mID 对应的所有内存单元,即便这些内存单元被分配在不同的块中。

实现 Allocator 类:

  • Allocator(int n) 使用一个大小为 n 的内存数组初始化 Allocator 对象。
  • int allocate(int size, int mID) 找出大小为 size 个连续空闲内存单元且位于 最左侧 的块,分配并赋 id mID 。返回块的第一个下标。如果不存在这样的块,返回 -1
  • int freeMemory(int mID) 释放 id mID 对应的所有内存单元。返回释放的内存单元数目。

示例:

输入
["Allocator", "allocate", "allocate", "allocate", "freeMemory", "allocate", "allocate", "allocate", "freeMemory", "allocate", "freeMemory"]
[[10], [1, 1], [1, 2], [1, 3], [2], [3, 4], [1, 1], [1, 1], [1], [10, 2], [7]]
输出
[null, 0, 1, 2, 1, 3, 1, 6, 3, -1, 0]

解释
Allocator loc = new Allocator(10); // 初始化一个大小为 10 的内存数组,所有内存单元都是空闲的。
loc.allocate(1, 1); // 最左侧的块的第一个下标是 0 。内存数组变为 [1, , , , , , , , , ]。返回 0 。
loc.allocate(1, 2); // 最左侧的块的第一个下标是 1 。内存数组变为 [1,2, , , , , , , , ]。返回 1 。
loc.allocate(1, 3); // 最左侧的块的第一个下标是 2 。内存数组变为 [1,2,3, , , , , , , ]。返回 2 。
loc.freeMemory(2); // 释放 mID 为 2 的所有内存单元。内存数组变为 [1, ,3, , , , , , , ] 。返回 1 ,因为只有 1 个 mID 为 2 的内存单元。
loc.allocate(3, 4); // 最左侧的块的第一个下标是 3 。内存数组变为 [1, ,3,4,4,4, , , , ]。返回 3 。
loc.allocate(1, 1); // 最左侧的块的第一个下标是 1 。内存数组变为 [1,1,3,4,4,4, , , , ]。返回 1 。
loc.allocate(1, 1); // 最左侧的块的第一个下标是 6 。内存数组变为 [1,1,3,4,4,4,1, , , ]。返回 6 。
loc.freeMemory(1); // 释放 mID 为 1 的所有内存单元。内存数组变为 [ , ,3,4,4,4, , , , ] 。返回 3 ,因为有 3 个 mID 为 1 的内存单元。
loc.allocate(10, 2); // 无法找出长度为 10 个连续空闲内存单元的空闲块,所有返回 -1 。
loc.freeMemory(7); // 释放 mID 为 7 的所有内存单元。内存数组保持原状,因为不存在 mID 为 7 的内存单元。返回 0 。

提示:

  • 1 <= n, size, mID <= 1000
  • 最多调用 allocatefree 方法 1000

正确写法

class Allocator:

    def __init__(self, n: int):
        self.size = n
        self.memory_busy = [False for _ in range(n)]

    def allocate(self, size: int, mID: int) -> int:
        count = 0
        temp_index = -1

        for i in range(self.size):
            if not self.memory_busy[i]:
                if count == 0:
                    temp_index = i
                count += 1
                if count >= size:
                    # 标记mID
                    for j in range(size):
                        self.memory_busy[temp_index + j] = mID
                    return temp_index
            else:
                count = 0

        return -1

    def freeMemory(self, mID: int) -> int:
        freed_blocks = 0
        for i in range(self.size):
            if self.memory_busy[i] == mID:
                self.memory_busy[i] = False
                freed_blocks += 1
        return freed_blocks

我的写法(太冗杂了)

class Allocator:

    def __init__(self, n: int):
        self.size = n
        self.memory_busy = [False for _ in range(n)]

    def allocate(self, size: int, mID: int) -> int:
        # 找到连续为False且长度>=size的串,然后将下标赋值mID
        # 开头是特殊情况特殊处理
        count_state = bool(self.memory_busy[0] == False)
        count = int(count_state)
        temp_index = 0

        for i in range(1,self.size):
            cur = self.memory_busy[i]
            pre = self.memory_busy[i-1]

            if count_state == True:
                # 如果由空闲区域变为非空闲区域
                if pre == False and cur != False:
                    count_state = False
                    if count >= size:
                        # 标记mID,memory_busy[temp_index:temp_index+size+1]为True
                        for i in range(size):
                            self.memory_busy[temp_index+i] = mID
                        return temp_index
                    count = 0
                # 连续为False就增加Count
                else:
                    count+=1

            else:
                # 如果由非空闲区域变为空闲区域
                if pre != False and cur == False:
                    count_state = True
                    count = 1
                    temp_index = i

            # 最后一轮进行额外判断
            if i == size-1:
                if count >= size:
                    # 标记mID
                    for i in range(size):
                        self.memory_busy[temp_index+i] = mID
                    return temp_index

        return -1



    def freeMemory(self, mID: int) -> int:

        return -1
        


# Your Allocator object will be instantiated and called as such:
# obj = Allocator(n)
# param_1 = obj.allocate(size,mID)
# param_2 = obj.freememory(mID)

2588. 统计美丽子数组数目

给你一个下标从 0 开始的整数数组nums 。每次操作中,你可以:

  • 选择两个满足 0 <= i, j < nums.length 的不同下标 ij
  • 选择一个非负整数 k ,满足 nums[i]nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1
  • nums[i]nums[j] 都减去 2k

如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0 的数组,那么我们称它是一个 美丽 的子数组。

请你返回数组 nums美丽子数组 的数目。

子数组是一个数组中一段连续 非空 的元素序列。

示例 1:

输入:nums = [4,3,1,2,4]
输出:2
解释:nums 中有 2 个美丽子数组:[4,3,1,2,4] 和 [4,3,1,2,4] 。
- 按照下述步骤,我们可以将子数组 [3,1,2] 中所有元素变成 0 :
  - 选择 [3, 1, 2] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [1, 1, 0] 。
  - 选择 [1, 1, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 0, 0] 。
- 按照下述步骤,我们可以将子数组 [4,3,1,2,4] 中所有元素变成 0 :
  - 选择 [4, 3, 1, 2, 4] 和 k = 2 。将 2 个数字都减去 22 ,子数组变成 [0, 3, 1, 2, 0] 。
  - 选择 [0, 3, 1, 2, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 2, 0, 2, 0] 。
  - 选择 [0, 2, 0, 2, 0] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [0, 0, 0, 0, 0] 。

示例 2:

输入:nums = [1,10,4]
输出:0
解释:nums 中没有任何美丽子数组。

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 106

异或+哈希

from typing import List
from collections import defaultdict

class Solution:
    def beautifulSubarrays(self, nums: List[int]) -> int:
        # 记录前缀异或出现的次数
        prefixXorCount = defaultdict(int)
        # 初始化前缀异或为0的情况出现了一次
        prefixXorCount[0] = 1
        
        xorSum = 0
        beautifulCount = 0
        
        for num in nums:
            xorSum ^= num
            # 如果当前的xorSum之前出现过,那么从之前的位置到当前位置之间的子数组就是一个美丽子数组
            if xorSum in prefixXorCount:
                beautifulCount += prefixXorCount[xorSum]
            
            # 更新当前xorSum出现的次数
            prefixXorCount[xorSum] += 1
            
        return beautifulCount

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值