最小替换字串长度 | 豆包MarsCode AI刷题

38 阅读4分钟

最小子串替换问题 - 详细解析

问题描述

给定一个只包含字符 A、S、D、F 的字符串,要求通过最少的替换操作,使得这四个字符在字符串中的出现频率相等。要求求出满足这一条件的最小子串长度。

解决思路

  1. 目标频次计算

    • 由于字符串的长度是 4 的倍数,因此可以计算出每个字符的目标频次。目标频次为 len(input) // 4,即每个字符应出现的频次。
  2. 字符频次统计

    • 使用 Counter 来统计字符串中每个字符的出现次数,找出哪些字符频次过高或过低。
  3. 滑动窗口策略

    • 我们的目标是找到一个最小子串,使得通过对其进行替换,所有字符的频次能够达到目标频次。采用滑动窗口的方式可以高效地计算出这个最小子串。

    滑动窗口的工作原理

    • 通过动态调整窗口的左右边界,维护窗口内字符的频次。在每次扩展右边界时,我们更新窗口内的字符频次,并检查是否满足替换条件。如果满足条件,收缩左边界,尝试更新最小窗口的大小。

实现步骤

  1. 统计字符频次:使用 Counter 来统计字符串中每个字符的出现次数。
  2. 计算目标频次:每个字符应有的目标频次为 len(input) // 4
  3. 计算需要替换的字符:如果某个字符的频次大于目标频次,则需要进行替换。
  4. 滑动窗口:通过滑动窗口动态调整窗口内的字符频次,检查是否满足替换条件,并记录最小窗口长度。

代码解析

def solution(input):
    from collections import Counter
    
    # 统计字符频次
    count = Counter(input)
    
    # 计算目标频次
    target_count = len(input) // 4
    
    # 计算需要替换的字符
    need_replace = {}
    for char in 'ASDF':
        if count[char] > target_count:
            need_replace[char] = count[char] - target_count
    
    # 如果没有需要替换的字符,直接返回0
    if not need_replace:
        return 0
    
    # 滑动窗口
    left = 0
    min_length = len(input)
    window_count = Counter()
    
    for right in range(len(input)):
        # 更新窗口内的字符频次
        window_count[input[right]] += 1
        
        # 检查窗口是否满足替换条件
        while all(window_count[char] >= need_replace.get(char, 0) for char in 'ASDF'):
            # 更新最小长度
            min_length = min(min_length, right - left + 1)
            
            # 移动左指针
            window_count[input[left]] -= 1
            left += 1
    
    return min_length

代码详解

  1. 字符频次统计

    count = Counter(input)
    

    这行代码使用 Counter 统计输入字符串 input 中每个字符的频次。

  2. 计算目标频次

    target_count = len(input) // 4
    

    由于字符串长度总是 4 的倍数,目标频次是字符串长度除以 4。

  3. 计算需要替换的字符

    need_replace = {}
    for char in 'ASDF':
        if count[char] > target_count:
            need_replace[char] = count[char] - target_count
    

    通过遍历字符集 'ASDF',找到哪些字符频次过高,并记录下需要替换的字符及其多余的数量。

  4. 滑动窗口处理

    window_count = Counter()
    left = 0
    min_length = len(input)
    for right in range(len(input)):
        window_count[input[right]] += 1
        while all(window_count[char] >= need_replace.get(char, 0) for char in 'ASDF'):
            min_length = min(min_length, right - left + 1)
            window_count[input[left]] -= 1
            left += 1
    
    • 右边界扩展:通过增加右边界来扩展窗口,并更新窗口内字符的频次。
    • 左边界收缩:当窗口内字符频次满足替换条件时,收缩左边界,并尝试找到最小子串。
  5. 返回最小窗口长度

    return min_length
    

    最后返回符合条件的最小窗口长度。

时间复杂度与空间复杂度

  • 时间复杂度O(n),其中 n 为字符串的长度。右边界遍历一次字符串,左边界仅在窗口满足条件时收缩一次,因此时间复杂度为线性。
  • 空间复杂度O(1),使用的额外空间是固定的,主要用于存储字符频次和窗口内的频次信息,字符集大小固定为 4,因此空间复杂度为常数。

总结

通过滑动窗口技术结合字符频次管理,可以高效地求解最小子串替换问题。该方法避免了暴力枚举所有子串,从而优化了时间复杂度,且能在常量空间内进行高效求解。