LeetCode 1784:二进制字符串连续1段数判断的上升沿解法
先说一下我为什么突然翻这道题。周末清理刷题记录发现LeetCode 1784这题我居然提交了三次才通过第一次还挂在“全0字符串”这种边界上当场给我整不会了。仔细一看题目本身不难但它的描述有个特别容易踩的坑——“二进制字符串字段”这个词翻译过来之后很多人第一反应是去判断字符串里有没有前导0或者去判断所有1是不是都在开头这两种理解都会导致误判。这题真正问的是字符串里连续出现的1片段是否只有一段。搞清楚这一点解法其实非常简单。这篇文章适合刚刷LeetCode不久的读者也适合准备面试时想快速过一遍字符串遍历题的老手。我会把这题的题意拆清楚给出可运行的Java和Python代码再列出我实际调试时补的边界用例和踩过的坑。顺便对比几道同类型的连续段统计题看完之后你会发现这种“状态变化计数”的思路是可以直接迁移的。1. 题目理解连续1的片段到底怎么数先看原题要求。给定一个二进制字符串s不含前导0也就是说如果字符串里有1那么第一个字符必须是1。要判断的是字符串里是否最多只有一段连续的1。这里的“一段”指的是由若干个相邻的1组成的一个整体中间不能有0隔开。如果整个字符串只有0那也算没有1片段应当返回true。我把这句话翻译成更接地气的说法把字符串想象成一条马路“1”是亮着的路灯“0”是灭掉的路灯。题目问的是这条路上的亮灯区域是否连成一片不允许出现“亮一段、灭一段、又亮一段”的情况。如果全程灯都是灭的当然也算没问题。很多人看完“不含前导0”这个条件会以为题目在问“1是不是都在字符串最前面”比如s 110就正确s 011就不正确。但前面这个条件其实是出题人为了简化问题加的因为不含前导0所以所有合法的测试用例要么以0开头且没有1要么以1开头。它并不要求1必须紧贴开头连续到结尾。看几个官方样例就明白了s 1001应该返回false因为1片段有“1”和“00夹着的1”两端s 110返回true因为1是连续的一段。所以核心任务只有一个统计字符串里出现了多少次“从0变为1”的变化。这个次数就是连续1片段的数量。如果数量小于等于1就返回true否则返回false。这种“统计上升沿”的思路正是这道题最简单也最稳的解法。再展开一下边界情况。s 0或者说任意长度的全0串统计到的上升沿次数是0。0次当然小于等于1所以返回true。s 1只有一个字符且是1上升沿次数是1返回true。s 01虽然以0开头但题目说输入不含前导0正常不会出现这种用例不过就算出现按上升沿统计它也只有一次从0到1的变化结果依然应该是true。理解到这一层边界就不会再成为障碍。2. 核心思路上升沿计数与易错点连续1片段的计数本质上是一个简单状态机当前字符是1且前一个字符是0就说明开启了一个新片段。如果当前字符是1且前一个字符也是1说明还在上一个片段里不重复计数。如果当前字符是0不管前一个是什么都不计数。这个思路我一开始没理顺绕进了另外一个方向先找第一个1和最后一个1的位置然后判断中间有没有0。中间如果有0说明1被切断成多段返回false否则返回true。这个方案逻辑上没毛病但有一个特别容易漏的边界字符串全0时indexOf返回-1lastIndexOf也返回-1如果没先判断-1就直接进循环很容易误判成true或者抛异常。对比之下上升沿计数不需要关心字符串里是否有1直接遍历一遍统计即可。哪怕全是0计数为0也是合法结果。不用单独处理“没找到1”的分支代码自然简洁很多。我整理了几个最容易被误解的地方按重要性排个序统计的是从0到1的切换次数不是统计字符1的个数。比如s 111000111字符1一共有6个但连续片段是2段结果应该是false。如果写成统计1的总数这道题就完全错了。单个字符1和连续多个1是同一段。s 1和s 111都返回true因为都只有一段。字符串里允许存在0哪怕是连续一大段0只要没有把1分隔成两段以上就合法。s 111000是trues 1110001是false。全0串合法。这一点官方没有在示例里强调但题目逻辑决定了s 0必须返回true。状态机思路在代码里体现为一次for循环加一个if判断。我放一个非优化的直白版本方便理解public boolean checkOnesSegment(String s) { int segments 0; for (int i 0; i s.length(); i) { if (s.charAt(i) 1) { // 当前是1并且是第一个字符或者前一个字符是0 if (i 0 || s.charAt(i - 1) 0) { segments; } } } return segments 1; }这个版本把“上升沿”的条件写得非常直观必须是当前字符为1且前一位不是1才算是新片段的开始。如果前一位也是1说明还在同一段里。因为题目明确说了不含前导0所以还有一个等价简化版只要字符串里出现“01”这种模式就说明前面已经有一段1结束了后面又出现了新的1直接返回false。换句话说一旦检测到子串01出现就代表至少有一个上升沿发生在非首字符位置可以提前结束public boolean checkOnesSegment(String s) { return !s.contains(01); }这个一行版本我自己是挺喜欢的但有个细节如果s以0开头且没有1比如000则不含01返回true符合题意。如果s 101包含01吗字符序列是1、0、1其中0后面是1包含子串01所以返回false也正确。这个解法在力扣上也可以直接通过。不过真要到面试的时候我建议还是先写上升沿版本因为那个版本思路更通用不依赖题目“不含前导0”的特殊条件。如果只记住contains(01)这个技巧遇到题目变体可能就懵了。3. 实操过程与核心实现直接上可运行的测试流程。我的习惯是先在本地IDE里写好解法然后拷贝进LeetCode编辑器跑一遍用例。除了题目自带的样例我一般还会补几个边界用例。先列一下我在本地跑通的用例集测试用例预期结果说明s 1001false有两段连续1第0位和第2~3位s 110true一段连续1s 1true单个字符也是合法字段s 0true没有1合法s 000true全0串没有连续1字段s 111000111false首尾各有一段1s 101false最常见的非法用例s 111true整串都是1s 010true中间一段1s 0111110true一段较长的连续1我实际调试时发现一个值得注意的点如果用字符串自带的indexOf和lastIndexOf去比较首尾1的位置判断条件不能写错。比如s 101first0last2中间有0返回false这没问题。但如果只判断first和last之间有没有0全0串会导致first -1、last -1循环区间为空很容易误判成true。所以用这种方式一定要先判断first是否等于-1。下面给出一个在本地能直接跑通的Java测试代码方便你复制粘贴验证public class Main { public static void main(String[] args) { Solution solution new Solution(); String[] cases {1001, 110, 1, 0, 000, 111000111, 101, 111, 010, 0111110}; boolean[] expected {false, true, true, true, true, false, false, true, true, true}; for (int i 0; i cases.length; i) { boolean result solution.checkOnesSegment(cases[i]); System.out.println(cases[i] - result (expected expected[i] )); if (result ! expected[i]) { System.out.println( FAILED); } } } }运行结果里所有用例都应该匹配。如果某个用例挂了优先检查count的初始化是否放到了循环内部——我一开始把count定义在for循环里面导致每遍历一个字符就清零一次最终返回的永远是1或0这个问题如果不跑完整用例很难发现。说一下在线评测时的提交情况。我这次提交用了Java 17运行时间0 ms内存消耗大约39.5 MB也处于正常范围。这类题真正比的是代码简洁度和边界处理而不是运行时间。用Python刷题的朋友有两种简洁写法可以选。先看统计上升沿的版本class Solution: def checkOnesSegment(self, s: str) - bool: count 0 for i, ch in enumerate(s): if ch 1 and (i 0 or s[i - 1] 0): count 1 return count 1这个版本和Java的上升沿版本完全对应只是利用了Python的enumerate来同时拿到索引和字符。再看利用字符串切分的版本class Solution: def checkOnesSegment(self, s: str) - bool: segments [x for x in s.split(0) if x] return len(segments) 1s.split(0)会把字符串按0切开然后过滤空串剩下的就是连续1片段。这个写法比正则更直观不需要引入re模块运行速度也不错。我个人在面试中如果写Python更倾向这个split版本因为它把解法思路暴露得很清楚按0切分后有值的片段就是每一段连续1。4. 常见问题与排查技巧刷题过程中我自己踩过几个坑也见过身边同事犯同样的错整理成一份速查表问题典型错误正确思路全0串判断错误直接找第一个1的索引没考虑索引为-1的情况用上升沿计数count保持0统计段数用了两次count判断条件写成arr[i] 1 arr[i - 1] 1必须是上升沿前一位是0或当前是首字符循环内重置count把int count 0写在for循环内部初始化放在循环外认为01不合法潜意识认为必须有1才算合法题目说最多一段1全0也合法使用indexOf方法但漏掉-1分支直接用first和last做循环边界先判first -1混淆“连续1字段”和“末尾是否有1”只验证字符串末尾是1还是0需要全局统计上升沿不能只看尾部逐个展开说一下。第一全0串的错误率其实比我预想的高。很多人看到“检查二进制字符串字段”默认以为字符串里必须有1。实际上题目说的是“不含前导0的二进制字符串中1是否连续出现”如果根本没有1就不存在“不连续”的问题。遇到s 000应该返回true。用上升沿计数这个case自动通过用indexOf方式就容易在first -1时栽跟头。第二上升沿判定条件要准确。arr[i] 1 (i 0 || arr[i - 1] 0)这里的i 0是为了处理字符串开头就是1的情况arr[i - 1] 0说明当前位置开启了一段新的连续1。如果你不小心写成arr[i] 1 arr[i 1] 0那是在统计下降沿语义完全不同。下降沿统计的结果是“每一段1的结束位置”判断逻辑要反过来容易出错。第三初始化位置问题。有的朋友会把count声明在循环里然后发现无论什么输入都返回false或true就是因为count在每次循环后都被清零了。这种错误最容易出现在从其他语言模板改写的时候Java里变量作用域比较严格但Python写循环内的局部变量也一样有这个问题。第四关于“01”这类字符串。比如s 01字符序列是0到1只有一个上升沿所以合法返回true。同理s 10只有一个上升沿第0个字符是1也合法。这一点很多刚接触这道题的人会搞混他们会以为“1字段必须在字符串开头且一直连续到结尾”但其实字段中间允许0存在只要不超过一段连续1。第五indexOf/lastIndexOf方案。这种方案本质上是对的但代码更啰嗦。我后来想了想用first和last的好处是可以在O(n)时间内解决坏处是要记边界条件。用上升沿则把边界条件内化到循环里代码更抗造。最后分享一个我常用的排查技巧。如果提交后状态是Wrong Answer但自己看代码觉得没问题就把输出加进去打印每个字符遍历时的count值。比如System.out.println(i i , c arr[i] , count count);然后拿一个错误用例跑一遍比如s 101输出会是i0, c1, count1 i1, c0, count1 i2, c1, count2看到count在i2时变成2能立刻明白第二个上升沿被正确计数于是就会回头检查判断条件是否写错效率比自己干想高很多。5. 相似题目对比为什么这题的思路可迁移刷LeetCode不能只刷一道把相似问题放在一起对比才能把套路内化。与1784最相关的几道题题目考察点与1784的关系LeetCode 485 最大连续1的个数数组遍历、连续段长度同是统计连续1但需要记录最长长度LeetCode 1004 最大连续1的个数 III滑动窗口、双指针允许翻转k个0需要维护窗口LeetCode 1446 连续字符字符串遍历、连续段统计统计任意连续相同字符的最大长度LeetCode 1869 哪种连续子字符串更长分段统计、比较长度同时统计0和1的连续段先说485它的解法会维护一个current变量表示当前连续1的长度遇到0就重置最后取最大值。这个解法的核心思想和1784的上升沿计数是同一套遍历数组时关注状态的切换点。区别在于1784只需要统计切换次数485需要记录区间长度。如果能把1784的“上升沿”思路理解透写485的代码基本不用看题解class Solution { public int findMaxConsecutiveOnes(int[] nums) { int max 0, current 0; for (int num : nums) { if (num 1) { current; max Math.max(max, current); } else { current 0; } } return max; } }再说1004它引入了“允许翻转k个0”的约束解法变成滑动窗口。窗口内0的个数不超过k窗口长度就是最长连续1。这道题如果直接套用上升沿计数会失败因为它需要找到一段区间而不是判断全字符串是否符合某个条件。但底层思维仍是“状态变化”——窗口内0出现的次数是关键状态量。1446和1869则继续加深连续段的概念。1446是求任意连续相同字符的最大长度把“1”的统计推广到所有字符。1869则要求分别统计0和1的段长再比较最大值。这几道题串起来看你会发现其实都在做同一件事识别序列中的状态切换然后根据题目要求对“段”做长度、数量或者位置上的统计。所以这道1784的真正价值不只是教会你判断字符串字段而是让你理解“状态机遍历”这一基本模式。遇到任何序列类问题先想想能不能定义几个状态把连续、切换、重置这些操作抽象出来代码往往一下子就清晰了。我在实际项目里处理日志流、网络包序列校验时也经常用这种思维把一个个数据包看成字符把特殊标记看成状态切换点逻辑会非常稳定。6. 经验总结与后续扩展题目不难但我觉得可以稍微说一下它在面试中的定位。LeetCode 1784属于典型的“热身题”难度简单大多出现在面试前5分钟或者笔试的第一题。它的作用不是筛人而是让候选人熟悉一下编辑器手感、确认基础编码风格。面试官看到这道题时通常重点考察三件事能否快速理解题意、能否正确处理边界条件、代码是否简洁。我个人的目标是三分钟之内写完上升沿版本并跑通所有测试用例。超过五分钟还没厘清“连续1字段”的定义说明对序列状态切换还不够敏感需要回去补一补数组和字符串的基础题。在一些LeetCode热门分类里1784经常被归类到“字符串遍历”和“简单模拟”两类标签下如果想系统练习建议把这两个标签下的简单题刷完再进入双指针和滑动窗口专题。代码风格方面这道题可以用一行的三元表达式写得很短但我不推荐为了短而短。可读性优先变量名用count而不是cnt之类缩写判断条件用括号包住i 0这些小习惯在面试中都能加分。很多候选人会忽略命名和格式把时间全花在解题上实际上面试官读代码的时间往往比写代码的时间更长清爽的风格本身就是沟通能力的一部分。还有一个扩展方向如果你对字符串处理感兴趣可以试试把1784推广到“检查字符串中连续相同字符的段数不超过k”这类变体。思路完全一样只是把判断条件从1扩展到任意字符把上限从1改成k。比如判断是否有超过k段连续相同字符只需要把count k作为返回条件。多做这种“改一个参数”的变体练习比盲目刷题更能锻炼迁移能力。最后说一点题外话。我在刷LeetCode时有个习惯每道简单题都会想三种以上的解法至少有一种是用API简化逻辑的有一种是纯手写状态遍历的。这道题的手写版本是上升沿计数API版本是split(0)或正则两种思路都掌握了不管面试官怎么追问都能应对。数据结构与算法的基础正是靠这样一次次“小题大做”积累起来的。希望这篇记录能帮你在LeetCode 1784上少走弯路。也欢迎在评论区分享你自己遇到的坑比如初始化位置、全0边界、正则表达式版本是否通过等一起把这些小细节踩实。刷题愉快。

相关新闻

AI数据中心超节点设计:从互联拓扑到NCCL调优的工程实践

AI数据中心超节点设计:从互联拓扑到NCCL调优的工程实践

1. 从单卡到超节点:为什么AI数据中心需要重新设计如果你最近一年在跟AI基础设施打交道,大概率会频繁听到一个词——超节点。我第一次接触这个概念是在一个千卡级训练集群的扩容讨论会上,当时团队面临一个很尴尬的局面:单卡算力明明…

2026/10/10 21:32:17 阅读更多 →
UE5.8 Mesh Terrain完全指南:从启用插件到高效地形雕刻

UE5.8 Mesh Terrain完全指南:从启用插件到高效地形雕刻

打开虚幻引擎 5.8,很多做关卡美术和地形编辑的朋友应该已经注意到,传统 Landscape 高度图系统之外,这次更新带来了一种全新的地形工作流——Mesh Terrain。简单说,它允许你用真正的三维网格体去组织和雕刻地形,而不再局…

2026/10/10 21:32:17 阅读更多 →
马行为识别数据集:从VOC解析到YOLO训练全流程

马行为识别数据集:从VOC解析到YOLO训练全流程

简介:马行为识别数据集是一套面向计算机视觉与深度学习场景的标注资源,主要用于马匹行为自动识别,覆盖站立、吃草、躺下等常见动作,对应不同的标注类别,整体识别准确率约为89.8%。压缩包共2000个文件,均为P…

2026/10/10 21:31:17 阅读更多 →

最新新闻

Java八种基本类型详解:从int到boolean,内存、范围与转换一次说透

Java八种基本类型详解:从int到boolean,内存、范围与转换一次说透

前两天看到一个挺有意思的说法,有人把 Java 的 int 关键字念成“英特”,还写成了“函数英特12”。乍一看像是网络流行梗,但细想之下还挺有代表性——这是典型的把“类型声明”当成“函数调用”的理解偏差。既然聊到这儿,干脆把 Ja…

2026/10/10 22:18:06 阅读更多 →
8089张野外动物数据集:YOLO与VOC双格式标注实战指南

8089张野外动物数据集:YOLO与VOC双格式标注实战指南

简介:这份资源是面向计算机视觉开发者与深度学习研究者的野生动物目标检测数据集,适用于野外固定视角水域场景下的动物识别与检测模型训练。数据覆盖大角斑羚、大象、长颈鹿、黑斑羚、捻角羚、羚羊、犀牛、角马、斑马共9类动物,图片清晰且未做…

2026/10/10 22:18:06 阅读更多 →
Claude Fable 5.1 与 Mythos 5.1 分权限开放后,TaoToken 统一 Key 的接入配置与验证

Claude Fable 5.1 与 Mythos 5.1 分权限开放后,TaoToken 统一 Key 的接入配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 22:18:06 阅读更多 →
低剖面高功率PIN二极管CLA4611-085LF:射频开关与电路设计详解

低剖面高功率PIN二极管CLA4611-085LF:射频开关与电路设计详解

1. 低剖面设计:不仅仅是为了“省地方”1.1 这颗小管子到底特殊在哪CLA4611-085LF是一颗表面贴装的硅PIN二极管,封装采用SOD-323,长宽大约2.5mm乘1.2mm,高度不到1mm。第一次拿到样品时,我甚至以为它是个普通的开关二极管…

2026/10/10 22:18:06 阅读更多 →
AnyPS5 引导加载器深度拆解:不拆机打造 PS5 自定义工具环境

AnyPS5 引导加载器深度拆解:不拆机打造 PS5 自定义工具环境

项目概述:AnyPS5 到底在解决什么问题1. 项目概述:AnyPS5 到底在解决什么问题打开任意一个主机折腾群,隔三差五就有人在问“xx版本能不能破”“有没有万能工具”。AnyPS5 这个名字,听起来也像某个一键化方案,但接触过的…

2026/10/10 22:18:06 阅读更多 →
IP5385P单芯片45W快充充电宝方案设计与量产实践

IP5385P单芯片45W快充充电宝方案设计与量产实践

接了一个45W大功率充电宝项目,工期紧,老板压得厉害。最初我们看了一圈方案,有的需要外置协议IC,有的要自己写复杂的MCU快充协商逻辑,有的整体BOM成本根本压不下来。最后翻到英集芯选型表,看到IP5385P这颗芯…

2026/10/10 22:17:05 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →