Leetcode 3389. 使字符频率相等的最少操作次数

1.题目基本信息

1.1.题目描述

给你一个字符串 s 。

如果字符串 t 中的字符出现次数相等,那么我们称 t 为 好的 。

你可以执行以下操作 任意次 :

  • 从 s 中删除一个字符。

  • 往 s 中添加一个字符。

  • 将 s 中一个字母变成字母表中下一个字母。

注意 ,第三个操作不能将 'z' 变为 'a' 。

请你返回将 s 变 好 的 最少 操作次数。

1.2.题目地址

https://leetcode.cn/problems/minimum-operations-to-make-character-frequencies-equal/description/

2.解题方法

2.1.解题思路

动态规划。统计s中各个字符的出现次数,使用长度为27的数组counts进行记录。最终每个字符出现的次数一定小于max(counts)。所以可以枚举0到max(counts)之间的每个targetCount,计算所有字符数变成0或者targetCount的最小操作数,最终统计所有targetCount的情况下的最小操作数,即为题解。

2.2.解题步骤

主函数步骤

  • 第一步,预处理。统计s中各个字符的出现次数,使用长度为27的数组counts进行记录。

  • 第二步,枚举所有的targetCount,调用getMinCnt获取每个targetCount情况下的最小操作数,并统计最小值,即为题解

根据targetCount求最小操作数函数步骤

  • 第一步,状态定义。dp[i]表示s字符串中按a-z的顺序前i个字符个数变为targetCount或者0的最小操作次数

  • 第二步,状态初始化。dp[0]=0,dp[1]=min(abs(targetCount-counts[1]),counts[1])

  • 第三步,状态转移。

    • 3.1.剪枝。当counts[i]==0,dp[i]=dp[i-1]

    • 3.2.第一种情况,假定第i-1个字符的字符数已经变成了targetCount或者0,此时dp[i]_1=dp[i-1]+min(abs(targetCount-counts[i]),counts[i])。

    • 3.3.第二种情况,假定第i-2个字符的字符数已经变成了targetCount或者0(此时的dp[i-1]不可用,只能用dp[i-2]),并假定第i-1个字符和第i个字符之间没有转换操作;此时dp[i]_2=dp[i-2]+min(abs(counts[i-1]-targetCount),counts[i-1])+min(abs(counts[i]-targetCount),counts[i])。

    • 3.4.第三种情况,假定第i-2个字符的字符数已经变成了targetCount或者0(此时的dp[i-1]不可用,只能用dp[i-2]),且满足counts[i]targetCount else counts[i-1];dp[i]_3=dp[i-2]+max(preOperation,targetCount-counts[i])

    • 3.5.最终的dp[i]=min(dp[i]_1,dp[i]_2,dp[i]_3)

  • 第四步,dp[26]即为当前targetCount情况下的最小操作数

3.解题代码

python代码

class Solution:
    # 函数任务:获取所有字符的数目变成targetCount或者0的最小操作次数
    def getMinCnt(self, targetCount:int, counts:list[int]) -> int:
        # 第一步,状态定义。dp[i]表示s字符串中按a-z的顺序前i个字符个数变为targetCount或者0的最小操作次数
        dp = [0] * 27
        # 第二步,状态初始化。dp[0]=0,dp[1]=min(abs(targetCount-counts[1]),counts[1])
        dp[1] = min(abs(targetCount - counts[1]), counts[1])
        # 第三步,状态转移。
        for i in range(2, 27):
            # 3.1.剪枝。当counts[i]==0,dp[i]=dp[i-1]
            if counts[i] == 0:
                dp[i] = dp[i - 1]
                continue
            # 3.2.第一种情况,假定第i-1个字符的字符数已经变成了targetCount或者0,此时dp[i]_1=dp[i-1]+min(abs(targetCount-counts[i]),counts[i])。
            val1 = dp[i - 1] + min(abs(targetCount - counts[i]), counts[i])
            # 3.3.第二种情况,假定第i-2个字符的字符数已经变成了targetCount或者0(此时的dp[i-1]不可用,只能用dp[i-2]),并假定第i-1个字符和第i个字符之间没有转换操作;此时dp[i]_2=dp[i-2]+min(abs(counts[i-1]-targetCount),counts[i-1])+min(abs(counts[i]-targetCount),counts[i])。
            val2 = dp[i - 2] + min(abs(targetCount - counts[i - 1]), counts[i - 1]) + min(abs(targetCount - counts[i]), counts[i])
            # 3.4.第三种情况,假定第i-2个字符的字符数已经变成了targetCount或者0(此时的dp[i-1]不可用,只能用dp[i-2]),且满足counts[i]<targetCount,第i-1个字符和第i个字符之间有转换操作;记preOperations为第i-1个字符个数变成targetCount或者0的最小操作数,preOperations=counts[i-1]-targetCount if counts[i-1]>targetCount else counts[i-1];dp[i]_3=dp[i-2]+max(preOperation,targetCount-counts[i])
            if targetCount > counts[i]:
                preOperations = counts[i - 1] - targetCount if counts[i - 1] > targetCount else counts[i - 1]
                val3 = dp[i - 2] + max(preOperations, targetCount - counts[i])
            else:
                val3 = inf
            # 3.5.最终的dp[i]=min(dp[i]_1,dp[i]_2,dp[i]_3)
            dp[i] = min(val1, val2, val3)
        # 第四步,dp[26]即为当前targetCount情况下的最小操作数
        return dp[26]

    def makeStringGood(self, s: str) -> int:
        # 思路:动态规划。统计s中各个字符的出现次数,使用长度为27的数组counts进行记录。最终每个字符出现的次数一定小于max(counts)。所以可以枚举0到max(counts)之间的每个targetCount,计算所有字符数变成0或者targetCount的最小操作数,最终统计所有targetCount的情况下的最小操作数,即为题解。
        # 第一步,预处理。统计s中各个字符的出现次数,使用长度为27的数组counts进行记录。
        counts = [0] * 27
        for c in s:
            counts[ord(c) - ord('a') + 1] += 1
        # 第二步,枚举所有的targetCount,调用getMinCnt获取每个targetCount情况下的最小操作数,并统计最小值,即为题解
        result = inf
        for targetCount in range(max(counts) + 1):
            thisMinCnt = self.getMinCnt(targetCount, counts)
            # print(targetCount, thisMinCnt)
            result = min(result, thisMinCnt)
        return result

4.执行结果

posted @ 2025-06-27 08:36  Geek0070  阅读(11)  评论(0)    收藏  举报