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

38 阅读3分钟

典型的滑动窗口问题。

初步想法是先遍历一遍字符串计算四个字母出现的频率,然后计算平均值获得一个字母应该出现的个数(这个个数当然也可以直接通过长度/4算出来。。。)

别人写的博客使用的是暴力法,直接按照最小窗口长度遍历字符串,不断增大窗口。也即是先用窗口为一的从头到尾遍历,然后再用2的遍历,十分暴力。

另外一个方法是使用滑动窗口算法,先来看看滑动窗口问题代码设置的思路:

  1. 初始化窗口:使用两个指针(leftright)来表示窗口的边界。
  2. 扩展窗口:将 right 指针向右移动,扩展窗口,直到窗口内的元素满足某个条件。
  3. 缩小窗口:如果窗口内的元素满足条件,尝试将 left 指针向右移动,缩小窗口,以找到更小的满足条件的子串。
  4. 记录结果:在每次缩小窗口时,记录当前窗口的大小,并更新最小窗口大小。

所以最关键的是要想清楚窗口内满足的条件到底是什么?这个也是最难想到的地方。

题目的要求是:处理完一个字串后,四个字母的数量将相同。这个情况仅在窗口外部的字母数量小于理想值的前提下才能成立。假设窗口外的A的数量大于理想值,无论窗口内如何操作都无法满足题目的要求。

因此在本题中,活动窗口的条件为:窗口外的字母出现的次数不大于理想值。转化为具体操作就是 <font style="color:rgb(51, 51, 51);background-color:rgb(248, 248, 248);">totalCount[i] - windowCount[i] > idealFreq</font>。这个外我认为是本题的精髓所在,当我们考虑活动窗口需要满足的条件时,不应该仅仅考虑窗口内的状态,在某些情况下也可以通过窗口外的状态判断是否记录结果。

用Go语言实现

具体而言,在编写滑动窗口的题目时,会先初始化两个指针,然后以右指针作为循环的判断条件对输入切片遍历。

为了判断窗口外的字母数量是否小于理想值,分别创建两个切片windowCount ,totalCount 记录窗口内的字母数量以及整个字符串的字母数量,以此通过两者的差值获取窗口外的字母数量。

package main

import (
    "fmt"
)

func solution(input string) int {
    // 构造字母到数字的索引
    charToIndex := make(map[byte]int)
    charToIndex['A'] = 0
    charToIndex['S'] = 1
    charToIndex['D'] = 2
    charToIndex['F'] = 3

    totalCount := make([]int, 4)
    length := len(input)
    for i := 0; i < length; i++ {
        totalCount[charToIndex[input[i]]]++
    }
    // Please write your code here
    windowCount := make([]int, 4)
    // 计算理想值
    ideaCount := length / 4
    minLen := length
    // 开始使用滑动窗口技术寻找最小值
    left, right := 0, 0
    for right < length {
        windowCount[charToIndex[input[right]]]++
        right++
        for left < right && isIdea(ideaCount, windowCount, totalCount) {
            minLen = min(right-left, minLen)
            windowCount[charToIndex[input[left]]]--
            left++
        }
    }
    fmt.Println(minLen)
    return minLen
}

// 辅助函数,用于判断当前窗口是否满足条件
func isIdea(ideaCount int, windowCount []int, totalCount []int) bool {
    for i := 0; i < 4; i++ {
        if totalCount[i]-windowCount[i] > ideaCount {
            return false
        }
    }
    return true
}

func main() {
    //  You can add more test cases here
    fmt.Println(solution("ADDF") == 1)
    fmt.Println(solution("ASAFASAFADDD") == 3)
}