最小替换子串长度 | 青训营X豆包MarsCode 技术训练营

34 阅读3分钟

问题

小F得到了一个特殊的字符串,这个字符串只包含字符ASDF,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得ASDF这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。

为了实现这个问题,我们需要找出如何通过最小的替换次数使得字符串中字符 'A'、'S'、'D' 和 'F' 的出现频率相等。由于字符串的长度总是4的倍数,目标是使得每个字符的出现频率达到相同的值。

思路:

  1. 字符串的长度总是4的倍数,这意味着字符串的长度可以被4整除,因此最终每个字符的出现频率应为 len(input) // 4

  2. 需要通过替换字符使得每个字符的频率接近 len(input) // 4。如果某个字符的频率过多,我们需要减少它的频率;如果某个字符的频率过少,我们需要增加它的频率。

  3. 核心问题:我们要找出一个最小的子串,通过替换其中的字符,使得最终每个字符的频率相等。

解决步骤:

  1. 计算每个字符的出现次数:通过统计字符串中字符的频率。
  2. 计算目标频率:目标频率是 len(input) // 4
  3. 计算多余字符的数量:如果某个字符的频率超过目标频率,记录这些多余的字符。
  4. 最小化替换子串的长度:使用滑动窗口技术,计算最小长度的子串,使得替换后能够使所有字符的频率相等。

代码实现:

from collections import Counter

def solution(input):
    n = len(input)
    target_count = n // 4  # 每个字符的目标频率

    # 统计输入字符串的每个字符的频次
    count = Counter(input)
    
    # 如果每个字符的频率已经是目标频率,则不需要做任何操作
    if all(count[char] == target_count for char in 'ASDF'):
        return 0

    # 滑动窗口法
    left = 0
    min_len = n  # 初始化最小长度为整个字符串长度
    excess = {char: count[char] - target_count for char in 'ASDF'}  # 记录多余字符的数量
    
    # 初始化滑动窗口
    for right in range(n):
        # 在窗口右端,加入字符并更新多余数量
        excess[input[right]] -= 1
        
        # 判断当前窗口是否可以使所有字符的频率平衡
        while all(excess[char] <= 0 for char in 'ASDF'):  # 如果所有字符的多余数量 <= 0
            min_len = min(min_len, right - left + 1)
            # 尝试缩小窗口
            excess[input[left]] += 1
            left += 1

    return min_len

# 测试样例
if __name__ == "__main__":
    print(solution("ADDF") == 1)
    print(solution("ASAFASAFADDD") == 3)
    print(solution("SSDDFFFFAAAS") == 1)
    print(solution("AAAASSSSDDDDFFFF") == 0)
    print(solution("AAAADDDDAAAASSSS") == 4)

解释:

  1. 统计字符频次:首先使用 Counter 来统计字符串中每个字符的出现次数。
  2. 计算目标频次:每个字符的目标频率是 n // 4,其中 n 是字符串的长度。
  3. 滑动窗口技术
    • 我们初始化滑动窗口的左右指针 leftright,并且维护一个字典 excess 来记录每个字符的多余数量。
    • 滑动窗口的右端扩展时,更新 excess 字典;当所有字符的 excess 值小于等于零时,表示可以通过当前窗口内的替换实现字符频率平衡。
    • 然后尝试缩小窗口,更新最小长度 min_len

复杂度分析:

  • 时间复杂度:O(n),其中 n 是字符串的长度。我们只需要一次遍历字符串来统计字符频率,并且滑动窗口的操作是线性的。
  • 空间复杂度:O(1),由于只需要固定大小的额外空间来存储字符频率和窗口信息。

测试结果:

print(solution("ADDF") == 1)  # 1
print(solution("ASAFASAFADDD") == 3)  # 3
print(solution("SSDDFFFFAAAS") == 1)  # 1
print(solution("AAAASSSSDDDDFFFF") == 0)  # 0
print(solution("AAAADDDDAAAASSSS") == 4)  # 4