算法-滑动窗口-串联所有单词的子串

算法-滑动窗口-串联所有单词的子串

1 题目概述

1.1 题目出处

https://leetcode.cn/problems/substring-with-concatenation-of-all-words/

1.2 题目描述

在这里插入图片描述
在这里插入图片描述

2 滑动窗口+Hash表

2.1 解题思路

  1. 构建一个大小为串联子串的总长的滑动窗口
  2. 为每个words中的子串创建一个hash表, <子串值,子串出现次数>
  3. 记录每次开始位置,从左往右遍历字符串s,每次截取words[0]长度的子串,和2中hash表对比,如果没有或者使用次数超限,就表示该组合无法构成,退出该次检测;否则继续检测,直到构成的串联子串长度满足要求或者已检测长度超限。构建成功时,记录下起始序号
  4. 一轮检测完成后,窗口向右滑动1个长度,继续下一轮检测

2.2 代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> resultList = new ArrayList<>();
        int windowSize = words[0].length();
        if (windowSize > s.length()) {
            return resultList;
        }
        int strLength = windowSize * words.length;
        if (strLength > s.length()){
            return resultList;
        }
        Map<String, Integer> wordMap = new HashMap<>();
        for (int i = 0; i < words.length; i++) {
            Integer subKeyTimes = wordMap.get(words[i]);
            if (null == subKeyTimes) {
                subKeyTimes = 0;
            } 
            wordMap.put(words[i], subKeyTimes + 1);
        }

        for (int i = 0; i <= s.length() - strLength; i++) {
            int result = getStart(s, words, strLength, windowSize, i, wordMap);
            if (result != -1) {
                resultList.add(result);
            }
        }
        return resultList;
    }

    private int getStart(String s, String[] words, int strLength, int windowSize, int first, Map<String, Integer> wordMap) {
        int start = -1;
        int length = 0;
        Map<String, Integer> useMap = new HashMap();
        for (int i = first; i < s.length() && length < strLength && i < first + strLength; i += windowSize) {
            String sub = s.substring(i, i + windowSize);
            Integer subKeyTimes = wordMap.get(sub);
            if (null == subKeyTimes) {
                return -1;
            }
            Integer useTimes = useMap.get(sub);
            if (null == useTimes) {
                useTimes = 1; 
            } else {
                useTimes += 1;
            }
            if (useTimes > subKeyTimes) {
                return -1;
            }
            useMap.put(sub, useTimes);
            length += windowSize;
            if (start == -1) {
                start = i;
            }
        }
        if (length == strLength) {
            return start;
        }
        return -1;
    }
}

2.3 时间复杂度

在这里插入图片描述
s.length=N,words.length=M ,时间复杂度O(N*M)

2.4 空间复杂度

O(M)

3 双滑动窗口+Hash表

3.1 解题思路

使用两个滑动窗口:

  1. 外层滑动窗口,每次移动大小为1,最多移动words[0]长度
  2. 内层滑动窗口,每次移动大小为words[0]

相当于把words[0]长度倍数的子串放在一起,用内层滑动窗口进行解析:

  1. 如果遇到当前子串不在目标字符串s,则舍弃之前记录,从下一个子串开始
  2. 如果遇到当前子串在目标字符串s内:
    1. 使用次数未超限,则记录使用次数、首个拼接子串下标、并累加拼接长度,再看是否已经拼接目标串联子串成功:
      • 如果成功则记录下标并移除首个子串,继续检测下一个子串;
      • 如果未成功,直接继续继续检测下一个子串;
    2. 使用次数超限,则不断移除已拼接子串内首个子串,直到次数不超限为止,并继续检测下一个子串

3.2 代码

class Solution {
     public List<Integer> findSubstring(String s, String[] words) {
            List<Integer> resultList = new ArrayList<>();
            int windowSize = words[0].length();
            if (windowSize > s.length()) {
                return resultList;
            }
            int strLength = windowSize * words.length;
            if (strLength > s.length()){
                return resultList;
            }
            Map<String, Integer> wordMap = new HashMap<>();
            for (int i = 0; i < words.length; i++) {
                Integer subKeyTimes = wordMap.get(words[i]);
                if (null == subKeyTimes) {
                    subKeyTimes = 0;
                }
                wordMap.put(words[i], subKeyTimes + 1);
            }

            for (int i = 0; i < windowSize; i += 1) {
                List<Integer> subResultList = getStart(s, strLength, windowSize, i, wordMap);
                if (subResultList.size() > 0) {
                    resultList.addAll(subResultList);
                }
            }
            return resultList;
        }

        private List<Integer> getStart(String s, int strLength, int windowSize, int first, Map<String, Integer> wordMap) {
            List<Integer> resultList = new ArrayList();
            int start = -1;
            int length = 0;
            Map<String, Integer> useMap = new HashMap();

            for (int i = first; i <= s.length() - windowSize; i += windowSize) {
                String sub = s.substring(i, i + windowSize);
                Integer subKeyTimes = wordMap.get(sub);
                if (null == subKeyTimes) {
                    // 子串找不到,清空useMap
                    useMap.clear();
                    start = -1;
                    length = 0;
                    continue;
                }
                length += windowSize;
                Integer useTimes = useMap.get(sub);
                if (null == useTimes) {
                    useTimes = 1;
                } else {
                    useTimes += 1;
                    useMap.put(sub, useTimes);
                    while (useTimes > subKeyTimes) {
                        // 子串使用次数超出存在的子串条数,删除第一个子串计数,直到不超出为止
                        String firstSub = s.substring(start, start + windowSize);
                        useMap.put(firstSub, useMap.get(firstSub) - 1);
                        useTimes = useMap.get(sub);
                        start = start + windowSize;
                        length -= windowSize;
                    }
                }
                useMap.put(sub, useTimes);
                if (start == -1) {
                    start = i;
                }
                if (length == strLength) {
                    // 成功凑成一个串联子串
                    // 记录开始下标
                    resultList.add(start);
                    // 移除首个子串
                    String firstSub = s.substring(start, start + windowSize);
                    useMap.put(firstSub, useMap.get(firstSub) - 1);
                    start = start + windowSize;
                    length -= windowSize;
                }
            }

            return resultList;
        }
}

3.3 时间复杂度

s.length=N,words.length=M ,时间复杂度O(N)
在这里插入图片描述

3.4 空间复杂度

O(M)

参考文档

  • 详细通俗的思路分析,多解法

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

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

相关文章

ES 7.6 - JAVA应用基础操作篇

ES 7.6 - JAVA应用基础操作篇 环境准备依赖配置 实体类准备使用说明索引/映射操作创建索引和映射索引和映射相关查询删除索引 文档操作插入数据更新数据删除数据批量操作 文档查询根据ID查询根据字段精准查询根据字段分词查询控制返回字段范围查询组合查询排序分页高亮搜索聚合…

springboot定时任务:同时使用定时任务和websocket报错

背景 项目使用了websocket,实现了消息的实时推送。后来项目需要一个定时任务&#xff0c;使用org.springframework.scheduling.annotation的EnableScheduling注解来实现&#xff0c;启动项目之后报错 Bean com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration of t…

HTML <template> 标签

实例 使用 <template> 保留页面加载时隐藏的内容。使用 JavaScript 来显示: <button οnclick="showContent()">显示被隐藏的内容</button><template><h2>Flower</h2><img src="img_white_flower.jpg" width=&q…

36、springboot --- 对 tomcat服务器 和 undertow服务器 配置访客日志

springboot 配置访客日志 ★ 配置访客日志&#xff1a; 访客日志&#xff1a; Web服务器可以将所有访问用户的记录都以日志的形式记录下来&#xff0c;主要就是记录来自哪个IP的用户、在哪个时间点、访问了哪个资源。 Web服务器可将所有访问记录以日志形式记录下来&#xff…

二级评论列表功能

一&#xff1a;需求场景 我的个人网站留言列表在开发时&#xff0c;因为本着先有功能的原则。留言列表只有一级&#xff0c;平铺的。 当涉及多人回复&#xff0c;或者两个人多次对话后&#xff0c; 留言逻辑看着非常混乱。如下图 于是&#xff0c;我就打算将平铺的列表&#…

用C/C++修改I2C默认的SDA和SCL针脚

首先要说明一点&#xff1a;Pico 有两个 I2C&#xff0c;也就是两套 SDA 和 SCL。这点你可以在针脚图中名字看出&#xff0c;比如下图的 Pin 4 和 Pin 5是 I2C1 的&#xff0c;而默认的 Pin 6 和 Pin 7 是 I2C0 的。 默认情况下是只开启了第一个 I2C&#xff0c;也就是只有 I2C…

【大虾送书第四期】《Python之光:Python编程入门与实战》

目录 ✨写在前面 ✨本书亮点 ✨强力推荐 ✨文末福利 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f990;专栏地址&#xff1a;免费送书活动专栏地址 写在前面 作为一种极其流行的编程语言&#xff0c;Python已经成为了当今最为重要的生产力工具之一。无论小学生…

Rancher2.5.9版本证书更新

一、环境 主机名IP地址操作系统rancher版本K8s-Master192.168.10.236Centos 72.5.9 二、更新证书 1、查看当前证书到期时间 2、进行证书轮换 [rootK8s-Master ~]# docker ps |grep rancher/rancher d581da2b7c4e rancher/rancher:v2.5.9 &q…

java八股文面试[JVM]——元空间

JAVA8为什么要增加元空间 为什么要移除永久代&#xff1f; 知识来源&#xff1a; 【2023年面试】JVM8为什么要增加元空间_哔哩哔哩_bilibili

使用ctcloss训练矩阵生成目标字符串

首先我们需要明确 c t c l o s s ctcloss ctcloss是用来做什么的。比如说我们要生成的目标字符串长度为 l l l&#xff0c;而这个字符串包含 s s s个字符&#xff0c;字符串允许的最大长度为 L L L&#xff0c;这里我们认为一个位置是一个时间步&#xff0c;就是一拍&#xff0…

【云原生】Docker的数据管理(数据卷、容器互联)

目录 一、数据卷&#xff08;容器与宿主机之间数据共享&#xff09; 二、数据卷容器&#xff08;容器与容器之间数据共享&#xff09; 三、 容器互联&#xff08;使用centos镜像&#xff09; 总结 用户在使用Docker的过程中&#xff0c;往往需要能查看容器内应用产生的数据…

数学建模(四)整数规划—匈牙利算法

目录 一、0-1型整数规划问题 1.1 案例 1.2 指派问题的标准形式 2.2 非标准形式的指派问题 二、指派问题的匈牙利解法 2.1 匈牙利解法的一般步骤 2.2 匈牙利解法的实例 2.3 代码实现 一、0-1型整数规划问题 1.1 案例 投资问题&#xff1a; 有600万元投资5个项目&…

Hbase分布式安装

一、环境准备 启动zookeeper 启动hdfs 二、安装 上传安装包 1、解压 tar -zxf hbase-2.2.2-bin.tar.gz -C /opt/installs/2、更名 mv hbase-2.2.2/ hbase3、配置环境变量 [roothadoop11 conf]# vim /etc/profile export HBASE_HOME/opt/installs/hbase export PATH$PATH:$…

构建高性能云原生大数据处理平台:融合人工智能优化数据分析流程

文章目录 架构要点优势与应用案例研究&#xff1a;基于云原生大数据平台的智能营销分析未来展望&#xff1a;大数据与人工智能的融合结论 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏…

某网站DES加密逆向分析实战

文章目录 一、抓包分析二、加密分析一、重写加密 一、抓包分析 分析站点&#xff1a; aHR0cDovL2VpcC5jaGFuZmluZS5jb20v 首先我们提交一下登陆信息&#xff1a; 搜索j_password查看加密函数: 把上图搜索到的encryptPassword函数拿出来分析一下&#xff1a; function encryptP…

免费的png打包plist工具CppTextu,一款把若干资源图片拼接为一张大图的免费工具

经常做游戏打包贴图的都知道&#xff0c;要把图片打包为一张或多张大图&#xff0c;要使用打包工具TexturePacker。 TexturePacker官方版可以直接导入PSD、SWF、PNG、BMP等常见的图片格式&#xff0c;主要用于网页、游戏和动画的制作&#xff0c;它可以将多个小图片汇聚成一个…

springboot服务注册到Eureka,端口总是默认8080,自己配置端口不生效

这段时间接手了一个公司的老项目&#xff0c;用的是SpringCloud&#xff0c;在我用的时候突然发现有一个服务&#xff0c;注册到Eureka后&#xff0c;界面显示的端口和实际Ribbon调用的实例端口是不一致的&#xff0c;后来我自己写了个端口获取了一下所有的实例信息&#xff0c…

C语言 数字在升序数组中出现的次数

目录 1.题目描述 2.题目分析 2.1遍历数组方法 2.2二分查找方法 2.3代码示例 数字在升序数组中出现的次数 这道题可以用遍历数组和二分查找来处理 1.题目描述 2.题目分析 题目中有一个关键信息&#xff0c;非降序数组&#xff0c;我们可以使用if语句来处理这个问题 if(…

C#矩阵XY排序

矩阵XY快速排序 using MyVision.Script.Method;public class MyScript : ScriptMethods {//struct MOTIONPOSXY_S{public double Pos_x;public double Pos_y;};//脚本执行该方法public bool Process(){//try{//脚本代码写在下方 List<double> PointX GetDoubleList(&qu…

二分查找逻辑

目录 二分查找 查找逻辑 题目练习 题目描述 代码示例 总结 二分查找 二分查找是我们经常使用的一种算法&#xff0c;他的逻辑是 在升序或者降序且无重复元素的数组中&#xff0c;比较目标值和数组中间值的方法&#xff0c;每次缩小一半的搜索范围&#xff0c;相比遍历可…