Problem: 438. 找到字符串中所有字母异位词
题目:给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

【LeetCode 热题 100】438. 找到字符串中所有字母异位词——(解法一)定长滑动窗口+哈希表
【LeetCode 热题 100】438. 找到字符串中所有字母异位词——(解法二)定长滑动窗口+数组

整体思路

这段代码同样用于在字符串 s 中查找所有模式串 p 的异位词。它采用了一种更为巧妙和高效的 可变大小滑动窗口 算法。与前两个版本(一个用 HashMap,一个用固定大小窗口的数组)相比,此版本的核心思想是维护一个“合法”的窗口,该窗口内的字符都是 p 所需要的。

算法的整体思路可以分解为以下步骤:

  1. 初始化“需求”计数器

    • 算法使用一个大小为 26 的整型数组 cnt。这个数组的含义非常关键:它首先被初始化为模式串 p 的字符频率,代表着我们“需要”的字符及其数量。
    • 例如,如果 p = "aab",则 cnt['a'-'a'] 为 2,cnt['b'-'a'] 为 1。
  2. 滑动窗口的扩张与收缩

    • 算法使用 leftright 两个指针来定义滑动窗口 [left, right]right 指针持续向右移动,以扩大窗口。
    • 扩张与“消耗”:当 right 指针扫过 s 中的一个字符 c 时,会执行 cnt[c]--。这可以理解为“消耗”掉了一个所需的字符 c
      • 如果消耗后 cnt[c]>= 0,说明这个字符是 p 所需要的,且目前窗口内该字符的数量尚未超出 p 的需求。
      • 如果消耗后 cnt[c] < 0,这说明窗口内已经包含了多余的字符 c(即超出了 p 的需求量)。
    • 收缩以维持“合法性”
      • 一旦 cnt[c] 变为负数,就触发 while 循环。这个循环的目的是收缩窗口的左边界 left,直到窗口再次变得“合法”。
      • 收缩时,将 left 指针指向的字符“归还”给计数器(cnt[s.charAt(left) - 'a']++),然后将 left 右移。
      • 这个过程会一直持续,直到我们刚刚加入的那个多余字符 c 的计数 cnt[c] 不再为负。
  3. 判定与记录结果

    • 在每一次 right 指针移动后(并可能伴随着 left 的移动),算法会检查当前窗口 [left, right] 的大小。
    • 如果窗口大小 right - left + 1 正好等于模式串 p 的长度 m,这意味着:
      1. 窗口内没有多余的字符(因为 while 循环保证了所有 cnt 值都 >= 0)。
      2. 窗口的总字符数正好是 m
    • 这两个条件同时满足的唯一情况就是:窗口内的字符频率与 p 完全匹配。因此,这是一个异位词,将其起始索引 left 加入结果列表。

这种方法的精髓在于,它不强制窗口大小始终为 m,而是灵活地收缩窗口以排出“无效”字符,一旦窗口在“有效”状态下长度恰好为 m,就找到了一个解。

完整代码

import java.util.ArrayList;
import java.util.List;class Solution {/*** 在字符串 s 中查找 p 的所有异位词的起始索引。* 此版本使用可变大小的滑动窗口和单个计数数组,非常高效。* @param s 主字符串 (假定只包含小写字母)* @param p 模式字符串 (假定只包含小写字母)* @return 一个包含所有匹配起始索引的列表*/public List<Integer> findAnagrams(String s, String p) {// ans 用于存储结果,即所有异位词的起始索引List<Integer> ans = new ArrayList<>();int n = s.length();int m = p.length();// 如果主串比模式串还短,直接返回空列表if (n < m) {return ans;}// cnt 数组作为字符频率计数器。// 初始时,它存储了模式串 p 中需要的各个字符的数量。int[] cnt = new int[26];for (char ch : p.toCharArray()) {cnt[ch - 'a']++;}// left 是滑动窗口的左边界int left = 0;// right 是滑动窗口的右边界,它将遍历整个主串 sfor (int right = 0; right < n; right++) {// 获取右边界字符并将其映射到索引int c = s.charAt(right) - 'a';// 将该字符的所需数量减 1,表示窗口“消耗”了该字符。cnt[c]--;// 关键逻辑:处理窗口内字符冗余的情况。// 如果 cnt[c] < 0,说明窗口内字符 c 的数量已经超出了 p 的需求。// 此时需要从左侧收缩窗口,直到 cnt[c] 恢复到 0。while (cnt[c] < 0) {// "归还" 左边界字符:将其在 cnt 数组中的计数加 1。cnt[s.charAt(left) - 'a']++;// 移动左边界,缩小窗口。left++;}// 检查窗口大小是否等于模式串的长度。// 因为 while 循环保证了窗口内没有多余字符(所有 cnt[i] >= 0),// 如果此时窗口大小恰好为 m,那么它必然是一个异位词。if (right - left + 1 == m) {ans.add(left);}}// 返回最终的结果列表return ans;}
}

时空复杂度

时间复杂度:O(N + M)
  1. 模式串频率统计for (char ch : p.toCharArray()) 循环遍历模式串 p 一次,时间复杂度为 O(M),其中 Mp 的长度。
  2. 滑动窗口遍历
    • 外层的 for 循环由 right 指针驱动,从 0n-1,所以 right 指针总共移动了 N 次。
    • 内层的 while 循环由 left 指针驱动。虽然它在 for 循环内部,但 left 指针也只是一直向右移动,永不后退。
    • 在整个算法的生命周期中,left 指针最多从 0 移动到 n
    • 每个字符 s.charAt(i) 最多被 right 指针访问一次,也最多被 left 指针访问一次。因此,两个指针的总移动次数是线性的,约为 2N
    • 所有数组操作 cnt[...]--cnt[...]++ 都是 O(1) 的。

综合分析
总的时间复杂度是预处理 p 的时间加上两个指针遍历 s 的时间,即 O(M) + O(N) = O(N + M)。这是一个非常高效的线性时间复杂度。

空间复杂度:O(k) 或 O(1)
  1. 主要存储开销:算法只使用了一个额外的整型数组 cnt
    • 该数组的大小是 26,这是一个固定的常数,代表了字符集的大小(k=26)。它不随输入 sp 的长度而改变。
  2. 结果列表ans 列表用于存储输出,其空间不计入算法的辅助空间复杂度。
  3. 其他变量n, m, left, right, c 等都占用常数空间 O(1)。

综合分析
算法所需的额外辅助空间主要由 cnt 数组决定。由于其大小是固定的,空间复杂度为 O(k),其中 k=26。因为 k 是一个常数,所以通常也称其空间复杂度为 O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/86636.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86636.shtml
英文地址,请注明出处:http://en.pswp.cn/web/86636.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

求区间最大值

题目描述 给定一个长度为 N 的数列&#xff0c;和 M 次询问&#xff0c;求出每一次询问的区间内数字的最大值。 输入描述 第一行包含两个整数 N,M&#xff0c;分别表示数列的长度和询问的个数。 第二行包含 N 个整数&#xff08;记为&#x1d44e;&#x1d456;&#xff09;&am…

调试HDMI音频能8通道播放声音

一、使用场景 我们是通过rk主控的hdmi接口播放音视频给到ite68051芯片解析出8声道数据,分别通过4路i2s的数据脚给给到fpga去解析 调试步骤: 1.根据相关手册配置hdmi输出,hdmi声卡注册,如下: hdmi0_sound: hdmi0-sound {status = "disabled";compatible = &qu…

PowerBI 柱状图显示MoM销量环比示例,以及解决相同列值时设置柱子颜色的问题

先看效果: 假设有Sales表: 1. 我们先给它新增一个计算列&#xff0c;显示销售日期的年月 销售日期YYYYMM YEAR(Sales[销售日期])*100 MONTH(Sales[销售日期]) 2. 然后新增一个计算表&#xff0c;用于保存当前最大的销售日期&#xff0c;和上一个月的日期 DateComparisonT…

【docker】构建时使用宿主机的代理

docker构建过程中报错: pip 下载失败 解决办法:传递宿主机的代理 把宿主机的 HTTP_PROXY/HTTPS_PROXY 传进去,导致容器内的 pip 依然连不上代理,下载 build-dependencies(比如 setuptools)就会失败。 下面两步即可解决: Docker 构建阶段,127.0.0.1:7890 指向的是 容…

[Java 基础]算法

什么是算法 程序 数据结构 算法 算法&#xff08;Algorithm&#xff09;就是解决问题的步骤&#xff0c;就像做菜的食谱一样&#xff0c;告诉计算机一步一步如何完成任务。 例如&#xff1a; 排序算法&#xff1a;把一堆数字从小到大排列搜索算法&#xff1a;在一堆数据里…

C++理解for循环 计算题三

计算a的值 #include <iostream> using namespace std; int main() { int a0;for(int i0;i<3;i){for(int j0;j<3;j){aij;}}cout<<"a的值是 "<<a<<endl; return 0; } 计算a的值 #include <iostream> using namespace std; int …

梳理React中的fiber架构

文章目录 产生背景核心概念工作原理工作流程优势特点 产生背景 在React16之前使用的虚拟DOM是数组的形式&#xff0c;又因为React本身是应用级框架&#xff0c;状态改变后并不能准确知道是哪个组件发生了改变&#xff0c;只能对整个应用进行diff协调&#xff0c;受限于虚拟DOM…

Modbus 数据模型:线圈、寄存器与功能码详解(二)

三、Modbus 功能码详解 3.1 功能码分类与作用 Modbus 功能码是 Modbus 通信协议中的关键组成部分&#xff0c;它如同一个 “指令指挥官”&#xff0c;在通信事务处理中扮演着核心角色。功能码占用 1 个字节的空间&#xff0c;取值范围为 1 到 255 &#xff08;0x01 - 0xFF&am…

多表连接查询:语法、注意事项与最佳实践

&#x1f517; 多表连接查询&#xff1a;语法、注意事项与最佳实践 多表连接是 SQL 的核心能力&#xff0c;用于关联多个表的数据。以下是深度解析&#xff0c;涵盖语法规范、性能陷阱及实战技巧&#xff1a; &#x1f4dc; 一、多表连接语法大全 1. 显式连接&#xff08;推荐…

使用Calibre对GDS进行数据遍历

在芯片的GDS数据里&#xff0c;使用Calibre对数据进行处理是非常常见的操作&#xff0c;但是GDS是一种和常规设计结构不太一样的一种数据&#xff0c;这里&#xff0c;通过这个小小的科普文章&#xff0c;一起看看怎么样在GDS里边做数据漫游吧&#xff01;闲言少叙&#xff0c;…

PyQtNode Editor 第二篇自定义可视化视图

在第一篇博客中,我们已经完成了 PyQtNode Editor 的基础环境搭建,并深入解析了自定义图形场景QDMGraphicsScene的实现原理。那个带有网格背景的场景就像一张空白的图纸,现在我们要在这张图纸上开始绘制真正的节点系统。 今天我们将聚焦于节点编辑器的核心数据结构设计,实现…

【扩欧应用】同余方程

与扩欧的联系 在同余方程的求解过程中&#xff0c;我们通常需要将方程转化为线性不定方程&#xff08;Diophantine 方程&#xff09;的形式&#xff0c;然后使用扩展欧几里得算法&#xff08;Extended Euclidean Algorithm, EEA&#xff09;求解。 同余方程是怎么转化为线性不…

结构化数据:NumPy 的结构化数组

文章目录 结构化数据&#xff1a;NumPy 的结构化数组探索结构化数组的创建更高级的复合类型记录数组&#xff1a;结构化数组的变体走向 Pandas 结构化数据&#xff1a;NumPy 的结构化数组 虽然我们的数据通常可以用同质数组很好地表示&#xff0c;但有时情况并非如此。本文将演…

phpcms 更换新域名更新栏目url和内容页url无法更新解决方法

更换域名后更新栏目url和内容页url还是无法更新为新的域名&#xff0c;手动把cache文件夹下能清除的缓存文件清除了还是不行&#xff0c;把数据库的缓存表内容清空了还是不行&#xff0c;问题在于栏目缓存并没有清除。 解决办法: (1)、找到文件&#xff1a;/caches/configs/sys…

玛哈特七辊矫平机:板材平整的精密卫士

在金属板材加工领域&#xff0c;表面平整度是衡量产品质量的核心指标之一。无论是汽车覆盖件、精密仪器外壳&#xff0c;还是建筑装饰板材&#xff0c;任何弯曲、波浪或翘曲都将严重影响后续加工精度、产品强度及美观度。七辊矫平机&#xff0c;凭借其独特的辊系结构设计&#…

融合聚类与分类的退役锂电智能分选技术:助力新能源汽车产业可持续发展

融合聚类与分类的退役锂电智能分选技术&#xff1a;助力新能源汽车产业可持续发展 关键词&#xff1a;退役锂离子电池分选 | 聚类分类融合 | 电化学阻抗谱(EIS) | 动态时间规整(DTW) | 多模态分类模型 新能源汽车 | 电池梯次利用 | 增量学习 | 数字孪生 | 联邦学习 | 双流特征…

jenkins中执行python脚本导入路径错误

&#x1f9fe; 问题一&#xff1a;ModuleNotFoundError: No module named jenkins &#x1f50d; 现象&#xff1a; 在本地运行正常&#xff0c;但在 Jenkins 中运行脚本时报错&#xff0c;提示找不到 jenkins 模块。 ❓ 原因分析&#xff1a; Python 默认只从当前目录或已…

华为云Flexus+DeepSeek征文 | 华为云ModelArts Studio实战指南:创建高效的AingDesk知识库问答助手

华为云FlexusDeepSeek征文 | 华为云ModelArts Studio实战指南&#xff1a;创建高效的AingDesk知识库问答助手 前言一、ModelArts Studio介绍1. 华为云ModelArts Studio简介2. 华为云ModelArts Studio主要特点3. 华为云ModelArts Studio主要使用场景 二、AingDesk介绍1. AingDes…

NLP基础1_word-embedding

基于github项目&#xff1a;https://github.com/shibing624/nlp-tutorial/tree/main 自然语言处理任务 1) 简单任务 拼写检查 Spell Checking 关键词检索 Keyword Search 同义词查找 Finding Synonyms 2) 中级任务 解析来自网站、文档等的信息 3) 复杂任务 机器翻译 Ma…

ClickHouse系列--BalancedClickhouseDataSource实现

clickhouse-jdbc中负载均衡数据源的实现。 基本逻辑如下&#xff1a; 1.通过配置的url串&#xff0c;来切分构造url列表&#xff1b; 2.通过一个定时线程任务&#xff0c;来不断的去ping url列表&#xff0c;来更新可用的url列表&#xff1b; 3.在可用列表中随机返回一个可用ur…