题目描述给定两个字符串s和p找到s中所有p的异位词子串返回这些子串的起始索引。答案顺序任意。异位词字符种类相同每个字符出现次数也相同只是顺序可以不同。例如s cbaebabacdp abc的答案是[0, 6]。最初思路方向是对的用int[128]做频次数组再在s上滑一个长度等于|p|的窗口。第一版额外维护了三个变量cnt当前窗口里已经放了几个字符len还没凑齐的量l左端下标右端i每进来一个字符就a[c]--a[c] 0时len--。然后用两套条件决定要不要从左边弹出窗口已经合法len 0或者窗口已经满长cnt p.length()。问题出在哪里同一轮里两套出窗逻辑可能各执行一次。改成else if之后abab/ab能得到[0, 1, 2]但根因还在。cnt在匹配成功后没有回到「当前窗口的真实长度」。以官方用例为例s cbaebabacd p abc下标0..2的cba是合法窗口。下一轮i 3进来的是e它不在p里窗口已经非法左端却不会继续往右缩e会留在窗口里。另外len一开始按p.length()用个数初始化真正改len时却只在a[c] 0时加减问的是种类刚凑齐或出窗后刚从齐变成缺。个数和种类混在同一个变量里。出窗时还容易把a[s1[left]] 0理解成「左端这个字母是不是p里要找的」。s aabp ab走到窗口aa时左端a确实是要找的字母但当时a[a]并不是0。这个判断真正在问这一类现在是不是刚好凑齐拆掉它之后会不会从「齐」变成「缺」。正确思路先固定一个核心不变量每次判断答案时候选窗口的长度必须恰好等于|p|。右端下标是i时候选窗口的左端为left i - p.length() 1当left 0说明窗口已经达到|p|先判断它是否为异位词再把s[left]移出为下一轮腾位置。因此不需要单独维护l和cnt。频次数组a可以理解为一张「欠债表」a[c]表示窗口还欠p多少个字符c正数窗口里还缺这个字符0数量刚好负数这个字符进多了或者它根本不在p中len只记录「还没凑齐的字符种类数」。初始化时p中每出现一种新字符len。进窗、出窗时只有某个种类在「缺」和「齐」之间切换才修改len。当候选窗口长度等于|p|且len 0时窗口就是异位词。因为所有必需字符都已凑齐而窗口总长度又没有多余位置不可能再混入其他字符。窗口含义每轮要判断的候选窗口是s[left .. i]。在窗口尚未达到|p|时只让字符进窗达到|p|后先判断当前窗口再移出左端字符。扩张与收缩时机扩张右端字符c s1[i]进窗先执行a[c]--。若减完刚好等于0说明这个字符种类从「还缺」变成「刚好」所以len--。收缩当left 0时先判断当前窗口再移出s1[left]。如果移出前a[s1[left]] 0说明这一类原本数量刚好移出后会重新缺一个所以先len再执行a[s1[left]]。这样每轮只进一个字符并在窗口满后只出一个字符。遇到e这种无关字符时它的欠债值会变成负数窗口仍会持续向右滑动直到它被移出。窗口内维护什么只维护欠债表a和欠债种类数len。多进来的字符、不在p里的字符都走负数不会误伤len。原来的cnt没有参与这套不变量可以删掉。手推过程s cbaebabacdp abc。一开始a[a]a[b]a[c]1len 3。i0 进 ca[c]0len2窗口未满 i1 进 ba[b]0len1窗口未满 i2 进 aa[a]0len0left0 记 0出 ca[c] 从 0 变 1len1 i3 进 ea[e]-1len 仍为 1left1 不出答案出 ba[b] 从 0 变 1len2e进来后窗口非法但左端仍会跟着右端移动不会停住。继续滑动到i 8时候选窗口是下标6..8的baclen再次变为0所以记录起点6。最终答案是[0, 6]。再看s aabp ab用来区分「是不是要找的字母」和「这一类是否刚好齐」i0 进 aa[a]0len1窗口未满 i1 进 aa[a]-1len 仍为 1left0 候选窗口是 aa不能记答案 出左端 a此时 a[a]-1不是 0len 不变 再执行 a[a]a[a] 变回 0 i2 进 ba[b]0len0left1 候选窗口是 ab记录起点 1左端那个a虽然属于p但在窗口aa中它是多出来的那个。出窗是否修改len取决于这一类在出窗前是否数量刚好而不是这个字符是否属于p。伪代码下面的伪代码与紧接的 Java 实现逐步对应统计欠债种类、进窗、窗口满后判定、出窗。函数 findAnagrams(s, p): ans - 空列表 a - 长度为 128 的数组初值 0 len - 0 对于 p 中的每个字符 c: 如果 a[c] 0: len - len 1 a[c] - a[c] 1 s1 - s 的字符数组 对于 i 从 0 到 s1.length - 1: a[s1[i]] - a[s1[i]] - 1 如果 a[s1[i]] 0: len - len - 1 left - i - p.length 1 如果 left 0: 如果 len 0: 把 left 加入 ans 如果 a[s1[left]] 0: len - len 1 a[s1[left]] - a[s1[left]] 1 返回 ansJava 代码对应实现只做了排版并去掉不参与逻辑的cntimport java.util.ArrayList; import java.util.List; class Solution { public ListInteger findAnagrams(String s, String p) { ListInteger ans new ArrayList(); int[] a new int[128]; int len 0; for (char c : p.toCharArray()) { if (a[c] 0) len; a[c]; } char[] s1 s.toCharArray(); for (int i 0; i s1.length; i) { a[s1[i]]--; if (a[s1[i]] 0) { len--; } int left i - p.length() 1; if (left 0) { if (len 0) { ans.add(left); } if (a[s1[left]] 0) { len; } a[s1[left]]; } } return ans; } }易错点同一轮不要既按「已经合法」出窗又按「已经满长」再出一次定长写法里进出窗口各一次就够了。遇到不在p中的字符窗口必须继续保持定长往前滑不能只右移一格就停。len要么始终表示还欠的种类要么始终表示还欠的个数不要混用。当前实现是种类进窗后 0才len--出窗前 0才len。a[s1[left]] 0不是在问「它是不是要找的字母」而是在问「这一类现在是否刚好齐」。先判定len 0再出窗。先出窗会把当前合法窗口拆掉答案会漏。没有参与不变量的变量本轮的cnt不要留在最终代码里。建议测试用例s cbaebabacd, p abc 期望[0, 6] 说明中间夹着不在 p 里的 e检查窗口会不会卡住s abab, p ab 期望[0, 1, 2] 说明连续重叠的合法窗口s aab, p ab 期望[1] 说明窗口 aa 中有多余的 a检查出窗时是否错误修改 lens aa, p aa 期望[0] 说明p 里有重复字符s af, p be 期望[] 说明字符种类完全不相交s a, p a 期望[0] 说明最短合法窗口复杂度分析时间复杂度O(|s| |p|)。初始化扫描一次p之后s中的每个字符进窗一次、出窗至多一次。空间复杂度欠债表int[128]是O(1)Java 实现中的toCharArray()会复制s因此整段代码的额外空间为O(|s|)。复盘本轮从「lcnt 两套出窗条件」收成「定长窗口 欠债种类数」。最值得记住的是a[c] 0描述的是种类刚齐或刚缺不是「这个字母在不在p里」非法字符靠负数和定长出窗自然被滑走。正确性可以用cbaebabacd里的e以及aab/ab时多余的a自检。
企业数字化 ERP 产品动态
相关推荐
Android 地图距离和实测对不上?格网与地面归算差在比例因子 图上距离和实测对不上?不是仪器坏,是两者尺度不同。格网与地面归算来补差。前言
做测量、工程放样、测绘内业的人,可能都遇到过这种诡异的现象:同一个两点之间,投影坐标系里算出来的距离,和你在现场用全站仪… · 2026/9/24 5:14:14
用DeepSeek-V2构建高可信私有知识库的完整实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 5:13:19
H3CNE实验手册:Wireshark抓包+STP可视化+协议级排障 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 5:13:13
AC5与AC6编译器对比:从armcc到armclang的Keil工程迁移指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 5:53:11
Nginx UI 在 Kubernetes 上的 Helm 安装部署指南:从裸机到集群的一站式实践 后端前端运维MCP 服务 【免费下载链接】nginx-ui Yet another WebUI for Nginx 项目地址: https://gitcode.com/gh_mirrors/ngi/nginx-ui 点击查看 免费下载 本指南完整讲解如何基于官方 Helm Chart 将 Nginx UI(含其内置 Nginx 实例的 Web 管理面板&am… · 2026/9/24 5:52:59
C 语言入门:函数、函数调用与递归 1. 引言
在 C 语言的学习路径中,函数是从「会写简单程序」走向「能写结构化程序」的分水岭。把一段逻辑封装成函数,不仅能避免重复代码,还能让程序更清晰、更容易维护。而递归(Recursion)则是建立在函数调用之上的一种… · 2026/9/24 5:52:59
老主板BIOS魔改实战:微代码替换、VT-d与CR3校验绕过指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 5:52:41
IGBT选型实战指南:从参数解析到项目避坑 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 5:52:28
自托管埋点分析平台选型指南:ClickHouse与Superset实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 5:52:22
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44