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

212 阅读3分钟

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

代码练习 — 豆包 MarsCode

问题描述

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

测试样例

  1. 输入: "ADDF"输出: 1
  2. 输入: "ASAFASAFADDD"输出: 3
  3. 输入: "SSDDFFFFAAAS"输出: 1
  4. 输入: "AAAASSSSDDDDFFFF"输出: 0
  5. 输入: "AAAADDDDAAAASSSS"输出: 4

算法原理与思路

问题分析

为了达到字符频次的平衡,每个字符 A、S、D、F 应该出现的次数是 target = len(input) / 4。我们要找到一个最小的子串,通过替换这个子串,使得所有字符的频次达到 target

解题思路

  1. 统计频次:首先统计输入字符串中每个字符的初始频次。
  2. 目标检查:如果所有字符的频次已经满足 <= target 的条件,直接返回 0
  3. 滑动窗口
    • 右指针 r 逐渐向右扩展,降低窗口内字符的频次。
    • 当窗口外的字符频次满足 <= target 时,尝试通过左指针 l 缩小窗口。
    • 通过记录窗口大小,找到最小满足条件的子串。

代码实现

Go 语言实现

package main

import "fmt"

// 解决最小替换子串长度问题的函数
func solution(input string) int {
    n := len(input)
    targetCount := n / 4 // 每个字符应出现的次数
    charMap := make(map[byte]int, 4) // 统计每个字符的出现次数

    // 初始化 charMap
    for i := 0; i < n; i++ {
        charMap[input[i]]++
    }

    // 检查当前频次是否已经满足目标频次
    if isBalanced(charMap, targetCount) {
        return 0
    }

    // 使用滑动窗口找到最小的可替换子串
    minLen := n
    l := 0
    for r := 0; r < n; r++ {
        charMap[input[r]]-- // 移动右指针,缩小当前字符的频次

        // 当满足条件时,尝试缩小窗口
        for isBalanced(charMap, targetCount) {
            minLen = min(minLen, r-l+1)
            charMap[input[l]]++ // 移动左指针,恢复左边界字符的频次
            l++
        }
    }

    return minLen
}

// 检查是否满足频次平衡
func isBalanced(charMap map[byte]int, target int) bool {
    return charMap['A'] <= target && charMap['S'] <= target && charMap['D'] <= target && charMap['F'] <= target
}

// 返回两个数中的较小值
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func main() {
    // 测试用例
    fmt.Println(solution("ADDF")) // 输出 1
    fmt.Println(solution("ASAFASAFADDD")) // 输出 3
}

Python 版本实现

def solution(input: str) -> int:
    n = len(input)
    target_count = n // 4  # 每个字符应出现的目标频次
    char_map = {'A': 0, 'S': 0, 'D': 0, 'F': 0}

    # 统计每个字符的初始频次
    for char in input:
        char_map[char] += 1

    # 检查是否已经满足平衡
    if is_balanced(char_map, target_count):
        return 0

    # 滑动窗口寻找最小的可替换子串
    min_len = n
    l = 0
    for r in range(n):
        char_map[input[r]] -= 1  # 缩小右边界字符的频次

        # 尝试缩小窗口,如果满足条件
        while is_balanced(char_map, target_count):
            min_len = min(min_len, r - l + 1)
            char_map[input[l]] += 1  # 恢复左边界字符的频次
            l += 1

    return min_len

# 检查是否满足平衡
def is_balanced(char_map, target):
    return all(count <= target for count in char_map.values())

# 测试案例
if __name__ == "__main__":
    print(solution("ADDF"))  # 输出 1
    print(solution("ASAFASAFADDD"))  # 输出 3

复杂度分析

  • 时间复杂度: O(n),因为滑动窗口内的左右指针各遍历每个字符最多一次。
  • 空间复杂度: O(1),只需要常数大小的空间来存储字符频次。