字符串处理核心:状态机与双指针算法深度解析
1. 从“数单词”到理解字符串处理的本质最近在辅导一些刚接触编程的同学时发现一个挺有意思的现象很多人拿到“计算一行单词长度”这样的题目第一反应是去翻书找有没有现成的“单词分割函数”。这本身没错但如果你只停留在调用split()然后循环输出len()的层面那可能就错过了这道题背后更重要的东西——对输入流和字符串底层逻辑的亲手把控。这道来自《信息学奥赛一本通》的1142题表面看是简单的输入输出和字符串处理实则是训练我们“亲手拆解数据”能力的绝佳起点。它强迫你离开高级函数的舒适区去直面最原始的字符序列理解空格如何作为分隔符以及程序如何“一个字符一个字符”地构建出对数据的认知。今天我们就来彻底拆解这道题不仅给出能AC的代码更要弄明白每一步“为什么这么做”以及在这个过程中那些容易栽跟头的细节。2. 题目深度解析边界条件与核心陷阱题目描述很短“输入一行单词序列相邻单词之间由1个或多个空格间隔请对应地计算各个单词的长度。” 但这里面藏着好几个需要明确的关键点也是评判程序是否健壮的核心。2.1 输入格式的明确与“一行”的含义首先“输入一行”这个描述在编程中需要精确化。在控制台或文件输入中“一行”通常意味着以换行符\n作为结束标志的一段字符序列。对于C可能是cin配合getline对于Python就是input()。这里的关键是我们必须一次性读入整行而不是用cin stringC或input().split()Python的默认行为因为后者会自动忽略开头的空白符并以下一个空白符空格、制表符、换行作为截断点这恰恰会让我们丢失“连续多个空格”这一关键信息。所以第一步必须是读取整行。2.2 “单词”的定义与分隔符的处理题目说“相邻单词之间由1个或多个空格间隔”。这意味着分隔符唯一只有空格 是分隔符不包括制表符\t或其他空白字符。这是一个简化但在严格的在线评测OJ环境下我们必须严格按照题意来。空格数量不定可能是1个也可能是多个。这直接否决了用“遇到一个空格就切分”的简单逻辑因为多个连续空格会产生空字符串片段这些片段不是单词必须被跳过。开头和结尾可能有空格输入的行开头和结尾也可能有空格。这些空格不构成单词在计算时必须被忽略。这是最常见的陷阱之一很多人的程序在遇到 hello world 这样的输入时就会出错。2.3 输出格式的精确对应“对应地计算各个单词的长度”意味着输出顺序必须与单词在输入行中出现的顺序严格一致。输出通常是以空格分隔的每个单词的长度。如果一行没有单词例如输入全是空格根据常规逻辑应该没有输出或者输出一个空行。这些细节需要在编码前就想清楚。3. 核心算法设计状态机与双指针法解决这类问题有两种主流的底层思想状态机State Machine和双指针Two Pointers。它们都不依赖于高级的字符串分割函数能让我们更透彻地理解过程。3.1 状态机思路清晰地刻画程序“思维”我们可以把程序读取字符的过程看作是在几个状态间切换状态0寻找单词开始初始状态。程序逐个读取字符如果遇到非空格字符说明找到了一个单词的开头记录当前位置并切换到状态1。状态1记录单词中。继续读取字符只要不是空格就认为字符属于当前单词持续累加长度或记录位置。一旦遇到空格说明当前单词结束输出其长度然后切换回状态0。这个思路逻辑非常清晰尤其适合用while循环和if-else来实现。它明确区分了“在单词外”和“在单词内”两种情形不容易出错。C状态机实现示例#include iostream #include string using namespace std; int main() { string line; getline(cin, line); // 读取整行 int n line.length(); int i 0; bool inWord false; // 状态标志是否处于单词内 int wordLen 0; while (i n) { if (line[i] ! ) { // 当前字符不是空格 if (!inWord) { // 如果之前不在单词内说明是单词开头 inWord true; wordLen 1; // 开始计数 } else { // 已经在单词内继续计数 wordLen; } } else { // 当前字符是空格 if (inWord) { // 如果之前是在单词内说明单词结束了 cout wordLen ; inWord false; wordLen 0; } // 如果之前就不在单词内说明是连续空格直接跳过 } i; } // 循环结束后检查是否最后一个字符是单词结尾即行末没有空格结尾 if (inWord) { cout wordLen; } return 0; }3.2 双指针思路高效定位单词边界双指针法更直观一些。我们用两个“指针”通常是整数索引i和j来在字符串上滑动。指针i负责寻找单词的起始位置。它不断向前移动跳过所有的空格直到指向一个非空格字符。此时i的位置就是单词的开始。指针j从i开始寻找单词的结束位置。它继续向前移动只要指向的不是空格就继续移动。当j指向空格或字符串末尾时j-1的位置就是单词的结束。计算单词长度j - i。输出长度后将i移动到j的位置即空格处然后重复步骤1开始寻找下一个单词。这种方法代码紧凑效率高是竞赛中的常用技巧。Python双指针实现示例line input().rstrip(\n) # 读取整行并去掉末尾可能的换行符input()通常已处理但更安全 n len(line) i 0 first_output True # 用于控制输出空格使格式更美观 while i n: # 阶段1跳过前导空格和单词间的多个空格 while i n and line[i] : i 1 if i n: # 如果跳完空格已经到字符串末尾说明没有单词了 break # 此时 line[i] 是非空格字符即单词开始 start i # 阶段2找到这个单词的结尾 while i n and line[i] ! : i 1 # 此时 i 指向了单词后的第一个空格或字符串末尾 word_len i - start if not first_output: print( , end) print(word_len, end) first_output False # 循环结束所有单词长度已输出。如果一行没有单词则无输出。 print() # 最后输出一个换行符合多数OJ的格式要求注意上面Python示例中使用了first_output标志来控制输出格式避免了末尾多一个空格。有些OJ系统对末尾空格不敏感但养成输出整洁的习惯总是好的。更简单的做法是用列表先存储结果最后用print(*length_list)一次性输出。4. 不同语言下的实现策略与避坑指南虽然算法思想通用但在不同编程语言中实现细节和可利用的工具库不同也会产生不同的“坑”。4.1 C/C 实现注重手动控制与效率对于C/C选手这道题是练习字符数组C风格字符串和string类操作的经典题。坑点1输入整行C风格用fgets(char_array, sizeof(char_array), stdin)。注意它会读入换行符\n处理时需要判断。C风格用getline(cin, str)。这是最推荐的方式简单安全。坑点2遍历与边界手动遍历时务必注意数组下标不要越界。在双指针法中内层while循环的条件i n至关重要。坑点3输出格式C中连续用cout len ;输出最后会多一个空格。虽然很多OJ接受但严格的题目可能判错。可以采用类似Python中的“首次输出”技巧。一个健壮的C双指针实现#include iostream #include string using namespace std; int main() { string s; getline(cin, s); int n s.size(); int i 0; bool isFirst true; // 是否是第一个输出的数字 while (i n) { // 跳过空格 while (i n s[i] ) i; if (i n) break; // 跳过空格后到末尾结束 // 找到单词结束位置 int j i; while (j n s[j] ! ) j; // 计算并输出长度 if (!isFirst) cout ; cout (j - i); isFirst false; i j; // i跳到当前单词结束的位置即空格处外层循环的i会使其进入下一个循环 } cout endl; // 输出换行 return 0; }4.2 Python 实现简洁背后的陷阱Python让这道题变得极其简单但正因为简单更容易忽略细节。“一行代码”的诱惑与问题print(*[len(w) for w in input().split()])这行代码利用了input()读取一行split()默认以任意空白字符空格、换行、制表符等分割并自动过滤掉空字符串列表推导式计算长度最后用*解包打印。对于本题它完全正确且优雅。因为题目明确分隔符是空格split()的行为完全符合要求。但是这里存在一个教学上的“陷阱”如果你只记住了这行代码而没有理解split()在没有参数和有参数时的区别下次遇到“以逗号分隔”或者“严格以单个空格分隔需保留空单词”的题目时就会出错。s.split(): 按任意空白字符分割并自动移除结果中的空字符串。s.split( ): 严格按单个空格字符分割。如果存在连续空格会产生空字符串。所以更严谨的教学代码应该这样写以明确意图line input() # 明确使用空格作为分隔符但这样会得到空字符串片段 parts line.split( ) # 因此需要过滤掉空字符串 words [part for part in parts if part ! ] # 再计算长度 lengths [len(word) for word in words] print(*lengths)虽然最终结果和split()一样但这个过程清晰地展示了“分割-过滤-计算”的步骤加深了对字符串处理的理解。4.3 其他语言如Java的注意点Java中常用的Scanner.nextLine()读取整行然后用line.split( )可以按一个或多个空格进行正则分割但同样要注意开头结尾的空格会导致空字符串。更稳健的做法是使用line.trim().split(\\s)先去掉首尾空格再按空白字符分割。但trim()可能会误伤首尾的非空格字符本题不会所以最根本的还是手动遍历或使用StringTokenizer类虽然已过时但思路清晰。5. 测试用例设计与调试技巧写出代码不算完能否通过所有边界情况的测试才是关键。自己设计测试用例是一个优秀程序员必备的习惯。针对本题必须设计以下几类测试用例普通情况hello world-5 5多个连续空格hello world-5 5开头有空格 hello world-5 5结尾有空格hello world -5 5首尾都有空格 hello world -5 5单个单词programming-11单个单词带空格 programming -11空行或纯空格或 - 无输出或输出一个空行长单词与短单词混合a bc def ghij-1 2 3 4调试技巧打印中间变量在状态机或双指针算法中在关键步骤后打印i,j,inWord,wordLen等变量的值观察其变化是否符合预期。可视化遍历在纸上画出字符串手动模拟你的算法用笔移动i和j指针这是理解算法最有效的方式。使用在线调试器如果环境允许使用IDE的调试功能单步执行观察变量和程序流程。6. 从本题延伸的常见变体与解题思路掌握了本题的核心可以轻松解决一系列变体问题这也是刷题举一反三的关键。变体1统计单词个数而非长度。这更简单只需要在发现一个单词开始时状态机中!inWord变为true或双指针中找到start时计数器加1即可。变体2以特定单个字符如逗号分隔。这时分隔符不是空格了。算法完全一样只需把判断条件从line[i] ! 改为line[i] ! ,。但要注意如果用split(,)连续逗号会产生空字符串需要根据题目要求决定是否保留。变体3分隔符是多种字符如空格、逗号、句号。此时判断“是否分隔符”的条件变成一个集合检查。例如if (delimiters.find(line[i]) string::npos)C或者if line[i] not in ,.Python。核心算法框架不变。变体4不仅输出长度还要输出单词本身。在记录长度的同时用substr(start, length)C或切片line[start:end]Python把单词子串也保存下来即可。变体5输入包含多行直到文件结束EOF。这是OJ常见格式。需要将整个读取和处理的逻辑包在一个while (getline(cin, line))或for line in sys.stdin:的循环里。每行独立处理输出各自的结果。通过这样一道看似简单的题目我们实际上深入探讨了字符串处理的基石输入缓冲、字符遍历、状态管理、边界条件处理。这才是学习算法和编程的正确姿势——不满足于AC而要理解每一行代码背后的“所以然”。下次再遇到字符串处理问题不妨先想想我的指针应该怎么走程序现在处于什么状态

相关新闻

数学建模在高速公路应急车道动态管控策略中的应用与仿真

数学建模在高速公路应急车道动态管控策略中的应用与仿真

1. 项目概述:从一道赛题看现实交通管理的痛点 每年一到数学建模竞赛季,无论是“华为杯”还是其他知名赛事,总有一两道题能精准地戳中社会运行的某个“痒点”,让参赛者从纯粹的数学世界跳出来,直面现实中的复杂系统。今…

2026/8/18 11:50:39 阅读更多 →
深入解析FUSE:用户态文件系统开发从原理到实践

深入解析FUSE:用户态文件系统开发从原理到实践

1. 从一次文件访问的“意外”说起 那天下午,我正调试一个需要读取大量小文件的程序。本地磁盘是SSD,按理说速度不慢,但程序启动时,那个加载进度条还是慢得让人心焦。我习惯性地打开系统监控,想看看是不是I/O瓶颈&#…

2026/8/17 9:58:10 阅读更多 →
企业级Agentic AI安全检测系统(ADR)架构设计与实战指南

企业级Agentic AI安全检测系统(ADR)架构设计与实战指南

1. 从“被动响应”到“主动狩猎”:为什么企业需要Agentic AI安全检测系统最近和几个负责企业AI平台安全的朋友聊天,大家普遍有个共识:传统的AI安全监控手段,在面对越来越“自主”的Agentic AI时,开始有点力不从心了。过…

2026/8/17 9:58:10 阅读更多 →

最新新闻

深入解析 @JsonProperty:为什么加了它才能接收到数据?

深入解析 @JsonProperty:为什么加了它才能接收到数据?

前言在日常开发中,你是否遇到过这样的情况:前端明明传递了参数,后端实体类也有对应的字段,但数据就是接收不到?特别是在使用像 cCoatSurStatusDesc 这样"不太规范"的字段名时,问题尤为突出。本文…

2026/8/18 11:49:54 阅读更多 →
离线电路仿真软件 CircuitJS1 完整指南:342 个现成电路,塞进一台不联网的电脑

离线电路仿真软件 CircuitJS1 完整指南:342 个现成电路,塞进一台不联网的电脑

离线电路仿真软件 CircuitJS1 完整指南:342 个现成电路,塞进一台不联网的电脑 【免费下载链接】circuitjs1 Standalone (offline) version of the Circuit Simulator with small modifications based on modified NW.js. 项目地址: https://gitcode.co…

2026/8/18 11:49:54 阅读更多 →
思源宋体免费商用3分钟上手:TTF格式7个字重一次打包,中文字体授权焦虑就此终结

思源宋体免费商用3分钟上手:TTF格式7个字重一次打包,中文字体授权焦虑就此终结

思源宋体免费商用3分钟上手:TTF格式7个字重一次打包,中文字体授权焦虑就此终结 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 上线前一周,设计师小…

2026/8/18 11:49:54 阅读更多 →
告别满屏空白:3分钟让Windows资源管理器显示iPhone照片的HEIC缩略图

告别满屏空白:3分钟让Windows资源管理器显示iPhone照片的HEIC缩略图

告别满屏空白:3分钟让Windows资源管理器显示iPhone照片的HEIC缩略图 【免费下载链接】windows-heic-thumbnails Enable Windows Explorer to display thumbnails for HEIC/HEIF files 项目地址: https://gitcode.com/gh_mirrors/wi/windows-heic-thumbnails …

2026/8/18 11:49:54 阅读更多 →
iOS Safari视频下载与M3U8流媒体处理完整指南

iOS Safari视频下载与M3U8流媒体处理完整指南

在 iOS 生态中,Safari 浏览器因其与系统的深度集成,提供了流畅的浏览体验,但其设计初衷是作为安全的网页浏览器,而非下载管理器。因此,用户经常遇到一个核心痛点:如何在 iPhone 或 iPad 的 Safari 中下载网…

2026/8/18 11:49:54 阅读更多 →
C# ModBus

C# ModBus

简单汇总一下不使用NModBus包的C#上位机侧的ModBus实现;1. ModBus Tcppublic class ModBusSerer {private int _index;private int _slaveIndex 1;private TcpClient _tcpClient;public NetworkStream _stream;public void Start(string ip, int port){_tcpClient new TcpCli…

2026/8/18 11:48:53 阅读更多 →

日新闻

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 如果你还停留在"看网课 不停暂停 截图 …

2026/8/18 0:00:57 阅读更多 →
思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查

思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查

思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 你是不是也经历过这种时刻:设计稿里…

2026/8/18 0:00:58 阅读更多 →
华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate

华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate

华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, …

2026/8/18 0:00:59 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/18 9:15:35 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 9:06:28 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/18 9:04:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/17 18:54:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/17 18:55:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/17 18:55:55 阅读更多 →