多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

算法通关手册题解:LeetCode 0722 删除注释——C++ 注释语法与双指针逐行模拟

算法通关手册题解:LeetCode 0722 删除注释——C++ 注释语法与双指针逐行模拟 教程文档知识库【免费下载链接】AlgoNote⛽️「算法通关手册」从零开始的「算法与数据结构」学习教程200 道「算法面试热门题目」1000 道「LeetCode 题目解析」持续更新中项目地址https://gitcode.com/gh_mirrors/le/AlgoNote点击查看免费下载本文是「算法通关手册」中 LeetCode 0722「删除注释」 题解的深入解读。题目要求删除一段 C 源码中的行注释//与块注释/* ... */并保持行结构原样返回。本文从注释语法与优先规则出发推导出基于块注释状态标志的逐字符模拟算法给出可直接运行的 Python 代码并结合复杂度分析与源码佐证帮助读者掌握这一类「跨行状态 逐行模拟」问题的通用解法。1. 题目信息与考查点题号0722LeetCode 编号完整题解位于 remove-comments.md标签数组、字符串原始文档标注难度中等输入形式source是一个字符串数组其中source[i]表示第i行源码等价于源码由\n分隔输出形式删除注释后以相同格式返回源代码数组删除后为空的字符串不输出即答案中每个字符串都非空在「算法通关手册」的 题解总览 中本题被归类为「数组、字符串」标签的中等难度题目属于字符串处理中典型的逐字符扫描与状态维护问题。2. 题目大意与注释语法规则给一个 C 程序删除程序中的注释。程序source是一个数组其中source[i]表示第i行源码每行源码由\n分隔。C 中存在两种注释风格行注释字符串//表示//及其右侧的其余字符都应被忽略块注释字符串/*表示从/*开始直到下一个非重叠出现的*/之间的所有字符都应被忽略。注意阅读顺序为从左到右「非重叠」意味着字符串/*/并不会结束块注释因为注释结尾*/与开头/*发生了重叠。规则中还明确了注释之间的优先级第一个有效注释优先于其他注释如果字符串//出现在块注释中它会被忽略不会开启新的行注释同样如果字符串/*出现在行注释或块注释中它也会被忽略。关于输入数据题目给出了明确的约束与保证1 source.length 10^30 source[i].length 80source[i]由可打印的 ASCII 字符组成每个块注释最终都会被闭合因此在行或块注释之外的/*总是开始新的注释给定的源码中不会出现单引号、双引号或其他控制字符例如source /* Not a comment. */;这类含字符串字面量的样例不会出现没有其他内容如定义或宏会干扰注释。最后一个关键特性是隐式换行符可以通过块注释删除。由于块注释可以跨越多行被块注释包裹的行间换行符也随之消失这一特性在示例 2 中体现得非常明显。3. 示例分析3.1 示例 1行注释与多行块注释共存输入: source [/*Test program */, int main(), { , // variable declaration , int a, b, c;, /* This is a test, multiline , comment for , testing */, a b c;, }] 输出: [int main(),{ , ,int a, b, c;,a b c;,}]原始代码编排如下/*Test program */ int main() { // variable declaration int a, b, c; /* This is a test multiline comment for testing */ a b c; }第 1 行的/*与第 6-9 行的/*表示块注释第 4 行的//表示行注释。删除注释后int main() { int a, b, c; a b c; }可以看到几个细节第 1 行被整个块注释删除直接不输出第 4 行// variable declaration之后的内容被忽略但行首的两个空格在//之前被保留因此输出 第 6-9 行的块注释跨 4 行删除后这几行全为空不输出输出结果与原文档给出的[int main(),{ , ,int a, b, c;,a b c;,}]完全一致。3.2 示例 2块注释吃掉换行符输入: source [a/*comment, line, more_comment*/b] 输出: [ab]原始的source字符串等价于a/*comment\nline\nmore_comment*/b。删除注释后被块注释包裹的两处换行符隐含换行符也被删除剩下的ab以换行符分隔成数组时就是[ab]。这正是「隐式换行符可以通过块注释删除」的实际体现也是本题区别于普通字符串题目的关键点块注释状态必须在行与行之间保持跨行状态。4. 解题思路基于块注释状态标志的逐字符模拟4.1 核心思想删除注释本质上是一个「逐字符扫描 状态维护」的过程。由于块注释可以跨越多行单靠逐行处理无法完成任务必须用一个跨行持久的状态标志记录当前是否处于块注释中。用状态机的视角来看整个处理过程只有两个状态正常代码状态不在块注释中此时可能遇到普通字符、/*进入块注释、//忽略本行剩余部分块注释状态此时唯一关心的是是否遇到*/退出块注释其余所有字符包括//、/*、换行一律忽略。4.2 实现步骤参照 remove-comments.md 中「思路 1模拟」的步骤使用一个标志in_block表示当前是否在块注释中遍历每一行的每个字符如果在块注释中查找*/结束块注释如果不在块注释中遇到/*进入块注释遇到//忽略该行剩余部分break否则将字符加入当前行如果当前行不为空且不在块注释中将其加入结果。这里有一个实现细节值得注意/*、//、*/都是双字符标记判断时必须同时检查i 1 len(line)防止越界并且匹配成功后下标要一次跳过两个字符i 2。4.3 为什么块注释中的//与/*会被忽略由于in_block状态为真时代码只会查找*/这一种双字符序列其余字符全部跳过因此块注释内部出现的//不会触发行注释逻辑、/*也不会嵌套开启新的块注释。这正是「第一个有效注释优先于其他注释」规则的代码级体现也符合题目保证「每个块注释最终都会被闭合」的设定。5. 完整代码实现以下代码完整继承自 remove-comments.md 的「思路 1」实现class Solution: def removeComments(self, source: List[str]) - List[str]: result [] in_block False # 是否在块注释中 current_line [] # 当前行的内容 for line in source: i 0 while i len(line): if in_block: # 在块注释中查找 */ if i 1 len(line) and line[i:i2] */: in_block False i 2 else: i 1 else: # 不在块注释中 if i 1 len(line) and line[i:i2] /*: # 进入块注释 in_block True i 2 elif i 1 len(line) and line[i:i2] //: # 行注释忽略该行剩余部分 break else: # 普通字符 current_line.append(line[i]) i 1 # 如果不在块注释中且当前行不为空加入结果 if not in_block and current_line: result.append(.join(current_line)) current_line [] return result6. 代码逐段拆解与正确性论证6.1 跨行状态变量in_blockin_block在每行循环结束后不会重置这是整个算法的灵魂。当一行以未闭合的/*结束时如示例 2 的第一行a/*comment下一行会带着in_block True继续扫描从而保证跨行块注释被完整跳过。6.2current_line的累积与冲刷current_line以字符列表形式累积普通字符避免频繁字符串拼接。每行处理完毕时若in_block True说明当前行正处在块注释内部这一行无论是空还是累积了内容理论上块注释内不会累积内容都不输出且不清空current_line若in_block False且current_line非空把当前行拼成字符串加入result并清空current_line若in_block False且current_line为空整行被注释或为空行不输出。注意current_line的清空时机只有行处理完毕且不在块注释中时才清空。如果一行以块注释开始并以块注释结束如x/*c*/y由于块注释内不会向current_line追加字符x、y会正确拼接在同一行中。6.3 双字符标记的边界判断所有双字符标记判断都带i 1 len(line)边界条件避免在行尾读取越界。匹配成功后的i 2保证不会重复消费已匹配的标记字符这是扫描类算法常见的易错点。6.4 正确性验证对照两个示例示例 1逐行执行结果输入行处理过程输出行/*Test program */第 0 位遇/*进入块注释随后遇*/退出current_line为空不输出int main()普通字符全部累积int main(){普通字符累积{// variable declaration行首两个空格累积遇//breakint a, b, c;普通字符累积int a, b, c;/* This is a test...testing */全程处于块注释状态不输出多行a b c;普通字符累积a b c;}普通字符累积}示例 2逐行执行结果输入行处理过程输出行a/*comment累积a遇/*进入块注释行末in_block True暂不输出line块注释状态整行跳过暂不输出more_comment*/b块注释状态中遇*/退出随后累积bab三行最终拼接出ab与题目输出[ab]一致验证了「块注释删除隐含换行符」的行为。7. 复杂度分析时间复杂度$O(n \times m)$其中 $n$ 是源代码的行数$m$ 是每行的平均长度。每个字符最多被访问一次两个双字符标记每次多消耗一个字符的检查仍为线性。空间复杂度$O(n \times m)$主要用于存储结果result辅助空间current_line与in_block为 $O(m)$ 与 $O(1)$。8. 思路小结一类「跨行状态模拟」题的通法本题看似是一道字符串处理题但其难点在于状态跨行块注释可以跨越多行因此单行内的正则替换或字符串分割无法正确处理。这类问题在「算法通关手册」的其他题解中也反复出现例如remove-comments.md 的姊妹题如 0727. 最小窗口子序列子串匹配同样需要在扫描中维护指针与状态手册的 04_string 章节从 字符串基础 讲起覆盖 Brute Force 朴素匹配、KMP 等字符串算法其对应的源码实现位于 codes/python/04_string/string_brute_force.py 与 codes/python/04_string/string_kmp.py读者可对照学习双指针逐字符扫描的通用技巧。解题通法总结凡是需要「根据前缀标记忽略一段文本、且标记可跨行」的题目都可以采用「持久状态标志 逐字符扫描」的框架——把跨行信息保存在状态变量中单行内只做局部判断行与行之间保持状态连续性。本题的in_block状态就是这一框架的典型实现掌握后即可迁移到其他词法扫描、注释剥离、语法高亮等场景。赞分享教程文档知识库【免费下载链接】AlgoNote⛽️「算法通关手册」从零开始的「算法与数据结构」学习教程200 道「算法面试热门题目」1000 道「LeetCode 题目解析」持续更新中项目地址https://gitcode.com/gh_mirrors/le/AlgoNote点击查看免费下载相关推荐删列造序LeetCode 0944题解AlgoNote 算法通关手册中的数组与字符串双指针模拟实战删列造序LeetCode 0944题解AlgoNote 算法通关手册中的数组与字符串双指针模拟实战 本篇题解来自「算法通关手册」AlgoNote项目教程文档知识库算法通关手册LeetCode 0006 Z 字形变换题解模拟法逐行重构与周期规律算法通关手册LeetCode 0006 Z 字形变换题解模拟法逐行重构与周期规律 Z 字形变换Zigzag Conversion是 LeetCode教程文档知识库AlgoNote 算法通关手册LeetCode 0763「划分字母区间」贪心 哈希表 双指针逐行精讲AlgoNote 算法通关手册LeetCode 0763「划分字母区间」贪心 哈希表 双指针逐行精讲 导读 本篇题解对应 AlgoNote算法通关教程文档知识库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表