力扣周赛第01弹----思维 与 dp

力扣周赛第01弹----思维 与 dp

一、成为 K 特殊字符串需要删除的最少字符数

题目

给你一个字符串 word 和一个整数 k

如果 |freq(word[i]) - freq(word[j])| <= k 对于字符串中所有下标 ij 都成立,则认为 wordk 特殊字符串

此处,freq(x) 表示字符 xword 中的出现频率,而 |y| 表示 y 的绝对值。

返回使 word 成为 k 特殊字符串 需要删除的字符的最小数量。

示例

输入:word = "aabcaba", k = 0

输出:3

解释:可以删除 2"a"1"c" 使 word 成为 0 特殊字符串。word 变为 "baba",此时 freq('a') == freq('b') == 2

提示:

  • \(1 <= word.length <= 10^5\)
  • \(0 <= k <= 10^5\)
  • word 仅由小写英文字母组成。

思路

  • ①因为题目中只有26个小写英文字母,所以最多只会出现26个频数
  • ②考虑将频数按照从小到达排序,枚举最后保存频数的最小值x
  • ③将剩余的大于x + k的频数,变成x + k,小于x + k的就直接保留,计算保留的最大值,然后用总频数 - 保留值,即为删除最小数量

代码

class Solution:
    def minimumDeletions(self, word: str, k: int) -> int:
        cnt = sorted(Counter(word).values())
        max_save = 0
        for i,x in enumerate(cnt):
            s = 0
            for y in cnt[i:]:
                s += min(y,x + k)
            max_save = max(max_save,s)
        return len(word) - max_save

二、求出所有子序列的能量和

题目

给你一个长度为 n 的整数数组 nums 和一个 整数 k 。一个整数数组的 能量 定义为和 等于 k 的子序列的数目。

请你返回 nums 中所有子序列的 能量和

由于答案可能很大,请你将它对 \(10^9 + 7\) 取余 后返回。

示例

输入: nums = [1,2,3], k = 3

输出: 6

解释:

总共有 5 个能量不为 0 的子序列:

  • 子序列 [1,2,3]2个和为3 的子序列:[3] 和 [1,2] 。
  • 子序列 [1,3] 1个和为3 的子序列:[3] 。
  • 子序列 [2,3] 1个和为3 的子序列:[3] 。
  • 子序列[1,2] 1个和为3 的子序列:[1,2]。
  • 子序列[3] 1个和为3 的子序列:[3] 。

所以答案为 2 + 1 + 1 + 1 + 1 = 6

提示:

  • \(1 <= n <= 100\)
  • \(1 <= nums[i] <= 10^4\)
  • \(1 <= k <= 100\)

思路一

  • ①设dp[i][j] 为从前i个数中选出子序列,子序列得到和为j的方案数的总和;

  • ②易知,dp[i][j] = dp[i - 1][j] * 2

  • ③同时, dp[i][j] = dp[i][j] + dp[i - 1][j - num[i]]

  • ④注意,dp[0][0] = 1的初始化

  • ⑤综上,可列出状态转移方程写出程序!

代码

MOD = int(1e9 + 7)
class Solution:
    def sumOfPower(self, nums: List[int], k: int) -> int:
        n = len(nums)
        dp = [[0 for _ in range(k + 1)]for _ in range(n + 1)]
        dp[0][0] = 1
        for i in range(n):
            for j in range(k + 1):
                dp[i + 1][j] = dp[i][j] * 2 % MOD
                if j >= nums[i]:
                    dp[i + 1][j] = (dp[i + 1][j] + dp[i][j - nums[i]]) % MOD
        return dp[n][k]

由于只用到了 i + 1i两个状态,所以第一位可以优化掉,不过第二维要从从后往前枚举!

MOD = int(1e9 + 7)
class Solution:
    def sumOfPower(self, nums: List[int], k: int) -> int:
        n = len(nums)
        dp = [0 for _ in range(k + 1)]
        dp[0] = 1
        for i in range(n):
            for j in range(k,-1,-1):  # 从后往前枚举
                dp[j] = dp[j] * 2 % MOD
                if j >= nums[i]:
                    dp[j] = (dp[j] + dp[j - nums[i]]) % MOD
        return dp[k]

思路二

  • ①对于nums = [1,2,3,4,5],k=3,考虑到如果我们知道1 + 2 = 3,那么包含 1、2 对和为k的子序列的贡献为 \(2^{5-2}\)

  • ②所以我们只要求出,和为k的,长度为m的子序列的个数cnt,就能求出贡献为 $ 2^{n - m} × cnt$

  • ③设dp[i][j][k] 为从前i个数中选出和为j,且长度为k的子序列的个数!

  • dp[i][j][k] = dp[i - 1][j][k] ->不选第i个物品

  • dp[i][j][k] = dp[i][j - nums[i]][k - 1] ->选第i个物品

代码

MOD = int(1e9 + 7)
class Solution:
    def sumOfPower(self, nums: List[int], k: int) -> int:
        n = len(nums)
        dp = [[[0] * (n + 1) for _ in range(k + 1)]for _ in range(n + 1)]
        for i in range(n + 1):
            dp[i][0][0] = 1
        for i in range(n):
            for j in range(1,k + 1):
                for c in range(1,n + 1):
                    if j >= nums[i]:
                        dp[i + 1][j][c] = dp[i][j][c] + dp[i][j - nums[i]][c - 1]
                        dp[i + 1][j][c] %= MOD
                    else:
                        dp[i + 1][j][c] = dp[i][j][c]
        ans = 0
        for i in range(1,n + 1):
            ans = (ans + pow(2,n - i,MOD) * dp[n][k][i]) % MOD
        return ans

这也是可以优化第一维的,这里就不再赘述!

posted @ 2024-03-17 15:39  gebeng  阅读(37)  评论(0)    收藏  举报