关于滑动窗口算法--最小替换字串长度

个人觉得日常遇到的关于滑动窗口的算法题主要分两种:

固定窗口大小的滑动窗口

在固定窗口大小的滑动窗口问题中,窗口的大小是预先定义好的,不会改变。这种类型的问题是相对简单的,因为一旦确定了窗口的大小,就可以直接遍历数组或列表,每次移动窗口一个元素的位置。常见的问题包括:

  1. 最大/最小子数组和:给定一个数组和一个固定大小的窗口,找到所有可能的窗口的最大/最小和。
  2. 窗口内元素的统计:例如,统计窗口内奇数或偶数元素的数量。
  3. 窗口内元素的频率统计:统计窗口内每个元素出现的次数。

解决这类问题的关键在于维护窗口内的状态,以便在窗口滑动时能够快速更新状态。通常可以使用队列、双端队列(deque)或哈希表来维护窗口内的状态。

动态窗口大小的滑动窗口

动态窗口大小的滑动窗口问题则更为复杂,因为窗口的大小不是固定的,而是根据某些条件动态变化的。这类问题通常涉及到在窗口内寻找满足特定条件的子数组或子串。常见的问题包括:

  1. 无重复字符的最长子串:找到最长的不包含重复字符的子串。
  2. 包含所有字符的最短子串:给定一个字符串和一个字符集合,找到包含所有字符集合中字符的最短子串。
  3. 滑动窗口内的特定条件:例如,找到窗口内所有元素的乘积小于等于给定值的最大窗口。

解决这类问题的关键在于如何高效地扩展和收缩窗口,同时维护窗口内的状态。常用的数据结构包括:

  • 双端队列(deque):用于维护窗口内元素的顺序,以便快速找到窗口的边界。
  • 哈希表:用于快速查找和更新窗口内元素的频率。
  • 前缀和/前缀最大/最小值:用于快速计算窗口内元素的和、最大值或最小值。

在解决动态窗口大小的滑动窗口问题时,通常需要维护一个滑动窗口的左右指针,并根据特定条件来移动这些指针。例如,当窗口内不满足条件时,移动左指针来缩小窗口;当窗口内满足条件但需要寻找更优解时,移动右指针来扩展窗口。一般常见的是右指针实现扩张,通过扩张不断找到符合要求的字符串,然后左指针实现收缩,寻求当前状态下的最小的边界。

下面关于动态的滑动窗口给出题目,感兴趣的可以看下(掘金链接):最小替换子串长度 - MarsCode

问题描述

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


测试样例

样例1:

输入:input = "ADDF"

输出:1

样例2:

输入:input = "ASAFASAFADDD"

输出:3

样例3:

输入:input = "SSDDFFFFAAAS"

输出:1

样例4:

输入:input = "AAAASSSSDDDDFFFF"

输出:0

样例5:

输入:input = "AAAADDDDAAAASSSS"

输出:4

求解代码:

java 复制代码
public class Main {
    public static int solution(String input) {
        //这是一个很久之前遗留的问题,代码的第一步,清除我的原来的依托的代码,不得不否认,现在也是依托
        if(input.length() < 4){
            return input.length();
        }

        int[] target = get(input);
        int[] window = new int[4];
        int left = 0, right = 0;
        int minLength = input.length();

        while (right < input.length()) {
            // 扩展窗口
            char ch = input.charAt(right);
            switch (ch) {
                case 'A' -> window[0]++;
                case 'S' -> window[1]++;
                case 'D' -> window[2]++;
                case 'F' -> window[3]++;
            }
            right++;

            // 检查窗口是否满足条件
            while (isValidWindow(window, target)) {
                minLength = Math.min(minLength, right - left);
                if(minLength == 0){
                    return 0;
                }
                // 缩小窗口
                char leftChar = input.charAt(left);
                switch (leftChar) {
                    case 'A' -> window[0]--;
                    case 'S' -> window[1]--;
                    case 'D' -> window[2]--;
                    case 'F' -> window[3]--;
                }
                left++;
            }
        }
        return minLength;
    }

    public static boolean isValidWindow(int[] window, int[] target) {
        for (int i = 0; i < 4; i++) {
            if ( target[i] > 0 && window[i] < target[i]) {
                return false;
            }
        }
        return true;
    }

    public static int[] get(String input){
        int len = input.length() / 4;
        int[] result = new int[4];
        for(int i = 0;i < input.length(); i++){
            char ch = input.charAt(i);
            switch (ch) {
                case 'A' -> result[0]++;
                case 'S' -> result[1]++;
                case 'D' -> result[2]++;
                case 'F' -> result[3]++;
            }
        }
        for(int i = 0; i < 4; i++){
            result[i] = result[i] - len;
        }
        return result;
    }

    public static void main(String[] args) {
        //  You can add more test cases here
        System.out.println(solution("ADDF") == 1);
        System.out.println(solution("ASAFASAFADDD") == 3);
    }
}
相关推荐
见未见过的风景7 分钟前
启动报错java.lang.NoClassDefFoundError: ch/qos/logback/core/status/WarnStatus
java·开发语言·logback
Algorithm157641 分钟前
REST模式是什么,以及其他架构风格
java·架构
小汤猿人类44 分钟前
docker数据卷
java·docker·容器
抓哇FullStack-Junior44 分钟前
设计模式——建造者模式
java·开发语言·设计模式·建造者模式
pl00201 小时前
C++对象数组&对象指针&对象指针数组
算法·对象数组·对象指针·对象指针数组
白#泽1 小时前
课上测试:商用密码接口实现
服务器·数据库·算法
sahuid1 小时前
Redis 中 IntSet 底层数据结构
数据结构·数据库·redis
Sthamansa1 小时前
Java学习笔记(13)——面向对象编程
java·笔记·学习
saku2 小时前
赛博斗蛐蛐?B站微博知乎大混战中学透GraphRAG
人工智能·后端·算法
野風_199602012 小时前
代码随想录第51天
算法