如何通过最小替换实现字符串平衡:问题分析与解决方案

75 阅读4分钟

如何通过最小替换实现字符串平衡:问题分析与解决方案

在这篇笔记中,我们深入探讨一个与字符串操作相关的问题:给定一个只包含字符 ASDF 的字符串,其长度是 4 的倍数,如何通过最少的字符替换,使得这四种字符的频次相等。本文从问题分析到解决方法,将分步讲解,并附上完整的代码与测试样例,帮助你彻底掌握该问题。

问题描述

给定一个字符串 input,它仅包含 ASDF,且长度总是 4 的倍数。我们的任务是通过尽可能少的替换,使得 ASDF 在字符串中出现的频次完全相等。

例如:

  • 输入 "ADDF",目标是将 ADF 的频次都调整为 1
  • 输入 "ASAFASAFADDD",目标是将频次调整为 3

解决思路

要解决这个问题,我们需要以下关键步骤:

1. 计算目标频次

由于字符串的长度始终是 4 的倍数,因此我们可以将总长度除以 4,得到每个字符的目标频次。 例如:

  • 对于长度为 12 的字符串,每个字符的目标频次是 3
  • 对于长度为 16 的字符串,每个字符的目标频次是 4

2. 初始频次统计

统计字符串中每个字符当前的出现次数,与目标频次进行比较。如果所有字符的当前频次都已经小于或等于目标频次,则不需要替换,直接返回 0

3. 滑动窗口优化

如果初始状态未达到目标平衡,可以使用滑动窗口方法在字符串中查找需要替换的最小子串:

  • 初始化两个指针 leftright,用来表示当前窗口的范围。
  • 不断扩大 right 指针,将窗口范围扩大。
  • 在每次扩大窗口后,判断当前窗口能否覆盖所有需要减少的字符。如果能覆盖,则记录当前窗口长度,并尝试收缩窗口(移动 left 指针),以找到更小的满足条件的子串。

4. 最小化子串长度

通过滑动窗口的双指针操作,动态调整窗口的范围,确保最终记录的子串长度最小。

实现代码

以下是完整的 Python 实现代码,充分利用了滑动窗口算法来解决问题:

python

from collections import Counter

def solution(input):
    n = len(input)
    target_count = n // 4  # 每个字符的目标频次
    count = Counter(input)  # 当前字符频次统计
    min_len = n  # 初始化最小子串长度为整个字符串

    # 检查是否已经满足条件
    def is_balanced(count):
        return all(count[char] <= target_count for char in "ASDF")

    # 如果已经平衡,不需要替换
    if is_balanced(count):
        return 0

    # 滑动窗口
    left = 0
    for right in range(n):
        count[input[right]] -= 1  # 窗口右边界字符频次减少

        # 检查窗口内是否满足条件
        while is_balanced(count):
            min_len = min(min_len, right - left + 1)
            count[input[left]] += 1  # 收缩左边界
            left += 1

    return min_len

测试样例与分析

以下是多个测试样例的输入与输出分析。

样例 1

  • 输入input = "ADDF"
  • 目标频次:每个字符出现 1 次。
  • 初始频率统计{'A': 1, 'D': 2, 'F': 1}
  • 滑动窗口找到的最小子串是 "DD",只需替换其中一个字符即可平衡。

输出1

样例 2

  • 输入input = "ASAFASAFADDD"

  • 目标频次:每个字符出现 3 次。

  • 初始频率统计

    {'A': 5, 'S': 3, 'F': 3, 'D': 1}
    

    • A 多 2,D 少 2。
  • 滑动窗口找到的最小覆盖子串是 "DDD",替换后即可平衡。

输出3

样例 3

  • 输入input = "SSDDFFFFAAAS"

  • 目标频次:每个字符出现 3 次。

  • 初始频率统计

    {'S': 4, 'D': 2, 'F': 4, 'A': 2}
    

    • S 多 1,D 少 1。
  • 滑动窗口找到的最小子串是 "SS"

输出1

样例 4

  • 输入input = "AAAASSSSDDDDFFFF"
  • 目标频次:每个字符出现 4 次。
  • 初始频率统计{'A': 4, 'S': 4, 'D': 4, 'F': 4}
  • 已经满足目标频次,无需替换。

输出0

样例 5

  • 输入input = "AAAADDDDAAAASSSS"

  • 目标频次:每个字符出现 4 次。

  • 初始频率统计

    {'A': 8, 'D': 4, 'S': 4, 'F': 0}
    

    • A 多 4,F 少 4。
  • 滑动窗口找到的最小覆盖子串是 "AAAA"

输出4

时间与空间复杂度分析

时间复杂度

  1. 初始频次统计:O(n)
  2. 滑动窗口遍历:O(n)

总时间复杂度为 O(n),非常高效。

空间复杂度

  • 使用了一个字典(Counter)来统计频率,空间复杂度为 O(1)(仅存储 4 个字符的频次)。

总结

  • 本问题是滑动窗口算法的经典应用之一,通过双指针动态调整窗口范围,可以高效地解决最小子串问题。
  • 核心思想是将大问题分解为子问题:逐步缩小需要替换的子串范围,同时保证不影响全局平衡。
  • 本文实现的解决方案不仅代码简洁,而且具备良好的时间复杂度,适合处理大规模输入。

通过本文的学习,相信你对滑动窗口算法以及字符串操作的优化有了更深刻的理解!