【算法】【优选算法】滑动窗口(下)

目录

  • 一、904.⽔果成篮
    • 1.1 滑动窗口
    • 1.2 暴力枚举
  • 二、438.找到字符串中所有字⺟异位词
    • 2.1 滑动窗口
    • 2.2 暴力枚举
  • 三、30.串联所有单词的⼦串
    • 3.1 滑动窗口
    • 3.2 暴力枚举
  • 四、76.最⼩覆盖⼦串
    • 4.1 滑动窗口
    • 4.2 暴力枚举

一、904.⽔果成篮

题目链接:904.⽔果成篮
题目描述:

题目解析:

  • 这道题给的题目挺长,但是简化后就是给你一个数组,在其中找到数组元素只有两个数的最长子串。
  • 提示中写了数组元素的范围是小于等于数组长度的。

1.1 滑动窗口

解题思路:

  • 当我们遍历数组的时候,我们要让子数组中元素种类变多,就让子数组尾向后走;
  • 让子数组中元素种类变少,就让子数组头向后走;这又是同向双指针问题。
  • 我们还要使用一个计数器来标记子数组中的元素种类个数。
  • 使用一个type数组来标记子数组中两个种类中的个数,以便后面对计数器操作。
  • 进窗口:当元素种类个数小于等于2的时候,就让type数组中fruits[right]下标的元素加一,并让right向后走,这里面要注意如果是第一次进type数组,要让计数器加一。
  • 出窗口条件:当计数器大于2的时候就出窗口。
  • 出窗口:每次让type数组中的fruits[left]下标的元素加一,并让left向后走,这里面要注意如果是fruits[left]下标对应type数组元素为0,要让计数器减一。
  • 更新结果:在出完窗口后的子数组一定是符合条件的,直接去原结果和子数组长度的较大值即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(n)
import java.util.*;
class Solution {
    public int totalFruit(int[] fruits) {
        int len = fruits.length;
        int[] type = new int[len]; 
        int ret = 0;
        int flag = 0;
        for(int left = 0, right = 0; right < len; right++) {
            if(flag <= 2) {
                if(type[fruits[right]] == 0) {
                    flag++;  
                } 
                type[fruits[right]]++ ;//进窗口

            }

            while(flag > 2) {//出窗口条件
                //出窗口
                type[fruits[left]]--;
                if( type[fruits[left]] == 0) flag--;
                left++;
            }
            ret = Math.max(ret,right - left + 1);//更新结果
        }
        return ret;
    }
}

1.2 暴力枚举

解题思路:

  • 使用两层for循环将每一个符合要求的子数组都枚举出来。
  • 我们也要使用一个计数器来标记子数组中的元素种类个数。
  • 使用一个type数组来标记子数组中两个种类中的个数,以便后面对计数器操作。
  • 这里面有细节处理问题:我们出第二层循环的时候有两种可能:
    • 我们数组遍历完了,并且计数器小于等于2,出循环这时的[ i, j ]都是符合要求的。
    • 我们计数器大于二出循环,这时只有[ i, j )是符合要求的(也就是j下标执向的是第三种类型的元素)。
  • 会超时。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(n)
import java.util.*;
class Solution {
    public int totalFruit(int[] fruits) {
        int len = fruits.length;
        int ret = 0;
        for(int i = 0; i < len; i++) {
            int[] type = new int[len];
            int flag = 0;
            for(int j = i; j < len; j++) {
                if(flag <= 2) {
                    if(type[fruits[j]] == 0) flag++;
                    type[fruits[j]]++;
                }
                if(j == len - 1 && flag <= 2) {
                    ret = Math.max(ret,j-i+1);
                    break;
                }
                if(flag > 2) {
                    ret = Math.max(ret,j-i);
                    break;
                }
            }
        }    
        return ret;
    }
}

二、438.找到字符串中所有字⺟异位词

题目链接:438.找到字符串中所有字⺟异位词
题目描述:

题目解析:

  • 字母异位词:其实就是将字符串中所有字母随机排序能构成的所有子串都互为异位词,如abc的异位词就有:abc,acb,bac,bca,cab,cba。
  • 这道题就是让我们求给出字符串s中子串,是字符串p的异位词的首字母下标构成的集合。
  • 两个字符串是不是异位词的判定方法:
    • 将两个字符串排序,然后遍历一一比较。Java提供的sort时间复杂度O(NlogN)。
    • 哈希原理:使用数组分别记录下字符串中每个字符的个数,比较数组是否相等即可。

2.1 滑动窗口

解题思路:
-当我们遍历字符串的时候,在保持子串的元素尽可能少的变化(进出字符的时候中间的字符不会变化),要让子串的长度变大,就让子串尾向后走,让子串的长度变小,就让子串头向后走,同向双指针。

  • 记录下p字符串的长度lenP,一个数组hash1记录每个字符个数。
  • 一个数组hash2记录当前子串每个字符个数。
  • 进窗口:当子串长度小于lenP的时候,进字符。
  • 出窗口条件:当子串长度不小于lenP的时候。
  • 出窗口:让left下标对应字符出数组(也就是hash2对应下标减1)。
  • 修改结果:在每次出窗口后都要看现在的子串长度是否等于lenP,和两个数组是否一样。一样就将当前的left进入结果数组。
  • 上述的思路最后由于我们需要比较数组所以我们的复杂度虽然是O(N)但是,每一次比较都有26次。我们可以使用下述优化方案:
    • 我们使用一个计数器,来统计有效字符的个数(也就是与p字符串中字符相同的,子串中字符个数)。
    • 当我们进窗口之后,当前hash2数组right下标的个数小于等于hash1中的值时,就计数器加1。
    • 当出窗口之前,当前hash2数组left下标的个数小于等于hash1中的值时,就计算器减1。
    • 最后只需要比较计数器与p的长度比较即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
import java.util.*;
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        char[] ss = s.toCharArray();
        char[] pp = p.toCharArray();

        List<Integer> ret = new ArrayList<>();
        int[] hash1 = new int['z' - 'a' + 1];

        int lenP = p.length();
        int lenS = s.length();
        for(int i = 0; i < lenP; i++)
            hash1[pp[i]-'a']++;
        int[] hash2 = new int['z' - 'a' + 1];
         
        for(int left = 0,right = 0; right < lenS; right++) {
            //进窗口
            hash2[ss[right] - 'a']++;
            if(right-left+1 > lenP) {//出窗口条件
                //出窗口
                hash2[ss[left] - 'a']--;
                left++;
            }
            //更新结果
            if( right-left + 1 == lenP) {
                if(Arrays.equals(hash1,hash2)) ret.add(left);
            } 
        }
        return ret;
    }
}

优化:
//时间复杂度:O(n)
//空间复杂度:O(1)
import java.util.*;
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        char[] ss = s.toCharArray();
        char[] pp = p.toCharArray();

        List<Integer> ret = new ArrayList<>();
        int[] hash1 = new int['z' - 'a' + 1];

        int lenP = p.length();
        int lenS = s.length();
        for(int i = 0; i < lenP; i++)
            hash1[pp[i]-'a']++;
        int[] hash2 = new int['z' - 'a' + 1];
       
         int count = 0;
        for(int left = 0,right = 0; right < lenS; right++) {
            //进窗口
            hash2[ss[right] - 'a']++;
            if(hash2[ss[right] - 'a'] <= hash1[ss[right] - 'a']) count++;
            if(right-left+1 > lenP) {//出窗口条件
                //出窗口
                if(hash2[ss[left] - 'a'] <= hash1[ss[left] - 'a']) count--;
                hash2[ss[left] - 'a']--;
                left++;
            }
            //更新结果
            if( right-left + 1 == lenP) {
                if(count == lenP) ret.add(left);
            } 
        }
        return ret;
    }
}

2.2 暴力枚举

解题思路:

  • 记录下p字符串的长度lenP,一个数组hash1记录每个字符个数。
  • 使用两层for循环,使用数组hash2记录下每次长度为lenP的子串的每个字符个数。
  • 比较两个数组,相等记录子串首元素下标。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
import java.util.*;
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        char[] ss = s.toCharArray();
        char[] pp = p.toCharArray();

        List<Integer> ret = new ArrayList<>();
        int[] hash1 = new int['z' - 'a' + 1];

        int lenP = p.length();
        int lenS = s.length();
        for(int i = 0; i < lenP; i++)
            hash1[pp[i]-'a']++;
         
        for(int i = 0; i < lenS; i++) {
            int[] hash2 = new int['z' - 'a' + 1];
            for(int j = i; j < i + lenP; j++) {
                if(j >= lenS) break;
                hash2[ss[j]-'a']++;
            }
            if(Arrays.equals(hash1,hash2)) ret.add(i);
        }
        return ret;
    }
}

三、30.串联所有单词的⼦串

题目链接:30.串联所有单词的⼦串
题目描述:

题目解析:

  • 由于我们的words数组中每一个字符串的长度都是一样的,所以这道题跟上一道题其实是一道题。

3.1 滑动窗口

解题思路:

  • 我们的解题思路跟上一道题是一样的,只不过这次要是有真正的容器HashMap来表示了。
  • 我们两个指针每次走的长度也是words[0]字符串的长度。
  • 窗口循环次数,也要变,我们需要循环words中字符串的长度次数,因为当我们循环了这么多次后,再次循环就跟第一次重复了。例如在words[0].length() == 2的时候,第一次使用窗口是每两个字母为一组,当第三次使用时其实就是将第一次分组后的第一个组除去剩下的。

题目代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
import java.util.*;
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> ret = new ArrayList<>();
        int len = words[0].length();
        HashMap<String,Integer> hash1 = new HashMap<>();
        for(String str : words) hash1.put(str,hash1.getOrDefault(str,0)+1);

        for(int i = 0; i <len; i++) {
            HashMap<String,Integer> hash2 = new HashMap<>();
            for(int left = i, right = i, count = 0; right <= s.length()-len; right += len) {
                //进窗口
                String in = s.substring(right,right+len);
                hash2.put(in,hash2.getOrDefault(in,0)+1);
                if(hash2.get(in) <= hash1.getOrDefault(in,0)) count++;
                //出窗口条件
                if((right - left) / len + 1 > words.length) {
                    //出窗口
                    String out = s.substring(left,left+len);
                    if(hash2.get(out) <= hash1.getOrDefault(out,0)) count--;
                    hash2.put(out,hash2.get(out)-1);
                    left += len;
                }
                if(count == words.length) ret.add(left);
            }

        }
        return ret;
        
    }
}

3.2 暴力枚举

解题思路:

  • 直接两层for循环即可,只不过第一层是遍历每一个字母,第二层是一组一组遍历(即每次走的长度不一样)。
  • 虽然时间复杂度也是O(n^2),但这个是真的NN,上面的滑动窗口是mN比这个小太多了,所以会超时。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
import java.util.*
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> ret = new ArrayList<>();
        int len = words[0].length();
        HashMap<String,Integer> hash1 = new HashMap<>();
        for(String str : words) hash1.put(str,hash1.getOrDefault(str,0)+1);

        for(int i = 0; i < s.length(); i++) {
            HashMap<String,Integer> hash2 = new HashMap<>();
           for(int j = i; j+len <= s.length(); j += len) {
                String in = s.substring(j,j+len);
                hash2.put(in,hash2.getOrDefault(in,0)+1);
                if(hash1.equals(hash2)) ret.add(i);
           }

        }
        return ret;
        
    }
}

四、76.最⼩覆盖⼦串

题目链接:76.最⼩覆盖⼦串
题目描述:

题目解析:

  • 这道题就是求在s字符串中的子串包含t中所有字符(个数也要一样)的最小子串。

4.1 滑动窗口

解题思路:

  • 其实这道题还是和第二题有异曲同工之妙。
  • 只需要使用两个hash表来记录个数即可,hash1记录t字符串中的每种字符的个数。
  • hash2记录[left,right]区间中的每种字符个数。
  • 使用count计数器来记录有效字符个数。
  • 进窗口:每个right的元素直接进窗口,进了窗口后判断当前字符是不是有效字符。
  • 出窗口条件:count计数器中有效字符大于等于t字符串长度。
  • 出窗口:出left元素,在出元素之前,判断出的元素是否是有效元素。
  • 更新结果:由于是要返回字符串,所以我们记录字符串的头尾下标,由于出窗口是要让count小于t长度,所以我们要在出窗口前更新结果。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
import java.util.*
class Solution {
    public String minWindow(String ss, String tt) {
        char[] s = ss.toCharArray();
        char[] t = tt.toCharArray();

        int[] hash1 = new int[128];
        for(char ch : t) {
            hash1[ch]++;
        }
        int[] hash2 = new int[128]; 

        int count = 0;
        int retLeft = 0;
        int retRight = 0x3f3f3f3f;
        for(int left = 0, right = 0; right < s.length; right++) {
            //进窗口
            hash2[s[right]]++;
            if(hash2[s[right]] <= hash1[s[right]]) count++;
            //出窗口条件
            while(count >= t.length) {
                if(count == t.length) {
                    if(right - left <= retRight - retLeft) {
                        retLeft = left;
                        retRight = right;
                    }
                }
                //出窗口
                if(hash2[s[left]] <= hash1[s[left]]) count--;
                hash2[s[left]]--;
                left++;
            }
           // System.out.println(count+" "+ t.length);
            
        } 
        if(retRight == 0x3f3f3f3f) {
            return "";
        } else {
            return ss.substring(retLeft,retRight+1);
        }
    }
}

4.2 暴力枚举

解题思路:

  • 直接使用两层for循环将每一种使count计数器等于t.length的结果(即s字符串中的子串包含t中所有字符)枚举出来即可。
  • 会超时。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
import java.util.*
class Solution {
    public String minWindow(String ss, String tt) {
        char[] s = ss.toCharArray();
        char[] t = tt.toCharArray();

        int[] hash1 = new int[128];
        for(char ch : t) {
            hash1[ch]++;
        }

        int retLeft = 0;
        int retRight = 0x3f3f3f3f;
       for(int i = 0; i < s.length; i++) {
        int[] hash2 = new int[128]; 
        int count = 0;
        for(int j = i; j < s.length; j++) {
            hash2[s[j]]++;
            if(hash2[s[j]] <= hash1[s[j]]) count++;
            if(count == t.length) {
                if(j-i < retRight-retLeft) {
                    retLeft =i;
                    retRight = j;
                }
                break;
            }
        }
       }
        if(retRight == 0x3f3f3f3f) {
            return "";
        } else {
            return ss.substring(retLeft,retRight+1);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/910184.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Node.js——fs模块-路径补充说明

1、相对路径&#xff1a; ./座右铭.txt 当前目录下的座右铭.txt座右铭.txt 等效于上面的写法../座右铭.txt 当前目录的上一级目录中的座右铭.txt 2、绝对路径 D&#xff1a;/Program File Windows系统下的绝对路径/usr/bin Linux系统…

征程 6E DISPLAY 功能介绍

1.功能概述 本文实现单路、多路 MIPI CSI TX 输出、IDU 回写、IDU oneshot 模式、绑定输出 VPS 数据等功能&#xff0c;此处主要介绍各 sample 的实现与使用方法。 2.软件架构说明 本文中绑定 VPS 输出功能基于 libvio API 实现&#xff0c;调用 libvio 提供的 API&#xff…

JS事件防抖函数封装通用代码片段

JavaScript 函数防抖是一种技术&#xff0c;用于解决在特定时间段内连续触发事件时产生的问题。当一个事件被触发时&#xff0c;通过设定一个特定的延迟时间&#xff0c;在这个延迟时间内如果事件再次触发&#xff0c;则重新计时。只有当事件没有在延迟时间内再次触发时&#x…

xshell连接不上linux的原因

1、首先我们确定好linux的配置&#xff0c;右键选择设置&#xff0c;将网络适配器设置成NAT模式 2、点击linux编辑&#xff0c;选择虚拟网络 打开以后选中自己要配置的服务 3、进入以后选中自己的服务&#xff0c;确保是NAT模式&#xff0c;然后配置好子网ip&#xff08;尽量ip…

题目练习之二叉树那些事儿

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ 知道了二叉树的结…

K8S篇(基本介绍)

目录 一、什么是Kubernetes&#xff1f; 二、Kubernetes管理员认证&#xff08;CKA&#xff09; 1. 简介 2. 考试难易程度 3. 考试时长 4. 多少分及格 5. 考试费用 三、Kubernetes整体架构 Master Nodes 四、Kubernetes架构及和核心组件 五、Kubernetes各个组件及功…

webrtc前端播放器完整案例

https://download.csdn.net/download/jinhuding/89961792

深圳新世联:氢能中的气体传感器应用

氢能作为一种替代能源&#xff0c;被认为是破解能源危机&#xff0c;构建清洁低碳、安全高效现代能源体系的新密码。氢能的开发与利用正在引发一场深刻的能源革命。在2024年《政府工作报告》中&#xff0c;“加快前沿新兴氢能产业发展”这一重要任务被明确提出。据预测&#xf…

电源完整性测试解决方案

电源完整性测试 RIGOL MSO5000电源完整性测试 引言 在过去数十年间&#xff0c;电子行业飞速发展&#xff0c;产品功能不断强大&#xff0c;特性日益丰富&#xff0c;为我们的生活带来了现代化的便利与享受。然而&#xff0c;随着越来越多的产品依赖微控制器来提供优异性能和…

高阶函数--python

高阶函数应当满足至少下面一个条件&#xff1a; 接受一个或多个函数参数 输出一个函数 下面用一个例子来理解高阶函数。 一、高阶函数 先看一个简单的函数 例一&#xff1a; 例二&#xff1a; 是高阶函数&#xff0c;因为满足条件&#xff0c;返回一个函数 并且有闭包&a…

Chrome与火狐哪个浏览器的隐私追踪功能更好

当今数字化时代&#xff0c;互联网用户越来越关注在线隐私保护。浏览器作为我们探索网络世界的重要工具&#xff0c;其隐私追踪功能的强弱直接影响到个人信息的安全。本文将对比Chrome和Firefox这两款流行的浏览器&#xff0c;在隐私追踪防护方面的表现&#xff0c;并探讨相关优…

详细分析WebStorageCache 基本知识

目录 1. 基本知识2. Demo 1. 基本知识 相关的源码如下&#xff1a;web-storage-cache WebStorageCache 是一个用于扩展 HTML5 的 localStorage 和 sessionStorage 的库&#xff0c;增加了超时时间管理和序列化功能。它可以存储 JSON 对象&#xff0c;并且在存储数据时可以方便…

AJ-Report:一款开源且非常强大的数据可视化大屏和报表工具

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和工作学习方法 AJ-Report是一个基于Java的开源报表工具&#xff0c;它集成了ECharts、Ant Design Vue等前端技术&#xff0c;致力于为企业提供一站式的数据可视化解决方案…

K3梅林系统 强制刷机方法

对于梅林系统升级过过程中出现的无限重启卡屏的解决方案 黄色字体对应于K3 目前机器 主要分成两个关键步骤&#xff1a;第一、进CFE&#xff1b;第二、用TFTP传入文件进行刷机。 第一&#xff1a; 1硬件网线直接连接K3路由LAN口。 2带有无线网卡的电脑需要屏蔽掉无线网卡&…

数据结构 ——— 链式二叉树oj题:相同的树

目录 题目要求 手搓两个链式二叉树 代码实现 题目要求 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 手搓两个链式二叉树 代码演示&…

对标 Windows Copilot 的 UOS AI,升级后更能打了

进入 2024 年&#xff0c;AI 应用迎来大爆发&#xff0c;不仅各类应用纷纷宣称“AI 赋能”&#xff0c;操作系统也不例外。前有 Windows Copilot&#xff0c;后有 Apple Intelligent&#xff0c;手机行业更是积极&#xff0c;各种 AI 手机纷纷发布。国产信创系统自然也不甘落后…

leetcode912.排序数组的题解

题目描述&#xff1a; 题目要求在不使用任何内置函数的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))。 笔者使用了快速排序&#xff0c;但是直接使用最原始的快速排序&#xff0c;有些特殊的测试用例会超时。 1&#xff09;如果数组本身基本有序&#xff0c;则使用原始…

迷你版VFB,极简的Freebasic开发IDE-VB7-vb6编程开发

支持Freebasic, Js, vbs, Html5开发&#xff0c;可以发布成控制台程序&#xff0c;EXE&#xff0c;标准DLL&#xff0c;OCX控件&#xff0c;网站 类似Vscode, Aardio&#xff0c;按键精灵一样的开发工具。 本来芳芳只是想做个按键精灵办公小工具&#xff0c;结果一下小心搞了一…

【综合案例】使用React编写B站评论案例

一、效果展示 默认效果&#xff0c;一开始默认按照最热进行排序 发布了一条评论 按照最新进行排序 按照最新进行排序 二、效果说明 页面上默认有3条评论&#xff0c;且一开始进入页面的时候是按照点赞数量进行倒序排列展示&#xff0c;可以点击【最热 、最新】进行排序的切换。…

SSL证书申请终极指南

SSL验证是确认网站或服务器提供的SSL 证书的真实性和有效性的过程。 SSL证书验证是确认网站或服务器提供的SSL证书的真实性和有效性的过程。SSL证书是用于在客户端&#xff08;例如Web浏览器&#xff09;和服务器之间建立安全连接的数字证书。它们对于确保通过互联网传输的数据…