第121场双周赛题解:揭秘算法竞赛中的数位挑战与解题策略

 需要多掌握解题套路

 比赛地址

100157. 大于等于顺序前缀和的最小缺失整数

class Solution:
    def missingInteger(self, nums: List[int]) -> int:
           # Step 1: Find the longest consecutive prefix
            i = 0  
            for i in range(1, len(nums)):
                if nums[i] != nums[i - 1] + 1:
                    break
            else:
                # Handle the case where the entire array is a consecutive prefix
                i += 1

            # Step 2: Calculate the sum of the longest consecutive prefix
            prefix_sum = sum(nums[:i])

            # Step 3: Find the smallest missing integer greater than the prefix sum
            missing = prefix_sum
            while missing in nums:
                missing += 1

            return missing

100168. 使数组异或和等于 K 的最少操作次数

class Solution:
    def minOperations(self, nums: List[int], k: int) -> int:
            # Step 1: Calculate the XOR of all elements in nums
            m = 0
            for num in nums:
                m ^= num

            # Step 2: Count the number of different bits between m and k
            xor = m ^ k
            count = 0
            while xor:
                count += xor & 1
                xor >>= 1

            return count

100159. 使 X 和 Y 相等的最少操作次数 

class Solution:
    def minimumOperationsToMakeEqual(self, x: int, y: int) -> int:
           # Queue will contain pairs (current_value, operations_count)
            queue = deque([(x, 0)])
            visited = set() # To keep track of already visited values

            while queue:
                current, operations = queue.popleft()

                # If we have reached the target, return the number of operations
                if current == y:
                    return operations

                # Ensure we do not visit the same number again
                if current in visited:
                    continue
                visited.add(current)

                # If current is divisible by 11, enqueue the divided result
                if current % 11 == 0:
                    queue.append((current // 11, operations + 1))

                # If current is divisible by 5, enqueue the divided result
                if current % 5 == 0:
                    queue.append((current // 5, operations + 1))

                # Always enqueue the results of adding or subtracting 1
                queue.append((current + 1, operations + 1))
                queue.append((current - 1, operations + 1))

            # If y is never reached, return -1 or some error value
            return -1

100163. 统计强大整数的数目

  1. 函数 numberOfPowerfulInt

    • 输入参数:整数 start, finish, limit 和字符串 s
    • 返回值:区间 [start, finish] 内符合条件的数字数量。
    • 实现:调用 dfs 函数两次来计算不超过 finishstart - 1 的符合条件的数字数量,然后相减得到结果。
  2. 数位 DP 函数 dfs

    • 输入参数:当前处理的数位索引 i,一个布尔值 is_limit 表示当前是否受到上界 t 的限制,以及当前考虑的数的字符串表示 t
    • 返回值:在给定限制下,从当前位开始能构造出的符合条件的数字数量。
    • 实现:
      • 首先检查边界条件,当剩下的位数等于 s 的长度时,只能填入 s,并根据是否受限制判断是否可行。
      • 对于每一位,根据是否受限制和 limit 的值确定当前位的可能数字范围。
      • 递归地调用 dfs 来处理下一位,并累计所有可能性的总数。
  3. 递归和缓存

    • dfs 函数使用递归来处理每一位,通过 @cache 装饰器对函数结果进行缓存,以避免重复计算相同状态的结果,从而提高性能。

总体而言,这个实现通过精确地处理每一位的可能性,并利用递归和函数缓存来有效地处理大范围的数据。这种方法在处理复杂的数位相关问题时非常有效,尤其是在涉及大数字时。

class Solution:
    def numberOfPowerfulInt(self, start: int, finish: int, limit: int, s: str) -> int:
        n = len(s)
        
        # 数位dp函数,使用缓存装饰器以优化性能
        @cache
        def dfs(i, is_limit, t):
            # 如果剩余数字的长度小于后缀s的长度,则不可能构成有效数字
            if len(t) < n:
                return 0
            
            # 当剩余的位数等于后缀s的长度,只能填入s
            if len(t) - i == n:
                # 如果当前是受限状态,则检查t的剩余部分是否大于等于s
                if is_limit:
                    return int(s <= t[i:])
                else:
                    # 如果不受限,只有一种情况,即填入s
                    return 1
            
            res = 0
            
            start = 0
            # 如果当前受限,则枚举的数字不能超过t的当前位数字
            if is_limit:
                end = int(t[i])
            else:
                end = 9
            
            # 枚举的数字还需受限于limit
            end = min(end, limit)
            
            # 枚举当前位可能的数字,并递归处理下一位
            for num in range(start, end+1):
                res += dfs(i+1, is_limit and num == int(t[i]), t)
            
            return res
        
        # 计算区间[start, finish]内符合条件的数字数量
        return dfs(0, True, str(finish)) - dfs(0, True, str(start-1))

is_limit and num == int(t[i]):这个表达式决定了在下一次递归调用中,是否仍然受到上界 t 的限制。

  • 如果当前位 num 等于 t 在位置 i 的数字,并且之前的位置已经受到限制(is_limitTrue),那么在下一位上仍然受到限制。
  • 如果 num 不等于 t[i] 或之前的位置没有受到限制,那么在下一位上不再受到限制。

下面举例说明这个过程: 

假设我们有 t = "5432"limit = 9,并且当前我们在第二位(假设索引从 0 开始),即 i = 1,之前的数位值是 5,与 t[0] 相等,所以到目前为止我们是受限的(is_limit = True)。现在我们要决定第二位 i = 1 的值。

  1. 如果我们选择 num = 4(即等于 t[1]):

    • 在下一次递归调用中,我们仍然受限于 t,因为到目前为止构造出的数字仍然与 t 的前缀相匹配。所以,is_limit 仍然为 True
  2. 如果我们选择 num = 3(即小于 t[1]):

    • 在这种情况下,我们已经偏离了 t 的对应位置。即使之前是受限的,现在我们可以认为后续的数位不再受到 t 的限制。因此,在下一次递归调用中,is_limit 将变为 False
    • 这意味着对于这个位置及后续位置上的数位,我们可以自由地选择任何不超过 limit 的数字,而不用担心超过 t
  3.  num > t[i] 的情况不会发生,在之前的判断中已经被排除了
# 如果当前受限制(is_limit),则枚举的数字不能超过t的当前位数字
if is_limit:
    end = int(t[i])
else:
    end = 9

你可能感兴趣的:(#,算法,算法)