【力扣每日一题 0803】LEETCODE 722. 删除注释 (模拟 正则表达式)

本文讲述了在C++代码中去除单行//和多行/**/注释的方法,涉及模拟遍历和正则表达式的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

用字符串数组去表示一段代码,数组的每个字符串元素代表一行代码。现在给这样一个数组,要求去除其中的C++风格的注释(只有单行注释"//“和多行注释”/**/",尤其需要注意多行注释的换行符的去除问题)。

例题

【LEETCODE 722】

算法描述

一道挺好的字符串模拟题,真的要写起来还是有点麻烦和小坑的。
大致处理思路是分成两种情况,一种是未进入多行注释,另一种是已进入多行注释。有点坑的地方在于换行的处理,可以用while循环和字符串截取来处理,手动控制是处理下一行代码还是继续处理当前行代码的剩余部分。
另外一种“投机取巧”的方法是利用正则表达式来完成。
感谢下题解区“子不语”的分享,学到了很多,但是代码中有一处修改,修改后的代码详见代码实现。
首先,正则式的写法在本题中应该采用非贪婪模式,然后先将数组转成一个字符串(注意添加换行符来分割),然后正则替换,再重新分割,注意去除空字符串。

代码实现

常规模拟思路:

class Solution {
public:
    vector<string> removeComments(vector<string>& source) {
        vector<string> ans;
        int n = source.size();
        bool flag = 0;
        string tmp;
        int i = 0;
        while (i < n) {
            string& s = source[i];
            if (flag == 0) {
                size_t pos1 = s.find("//");
                size_t pos2 = s.find("/*");
                if (pos1 <= pos2) {
                    if (pos1 == string::npos) {
                        if (s.size())
                            ans.push_back(s);
                    } else if (pos1 != 0)
                        ans.push_back(s.substr(0, pos1));
                    i++;
                } else {
                    tmp = s.substr(0, pos2);
                    s = s.substr(pos2+2, s.size()-pos2-2);
                    flag = 1;
                }
            } else {
                int pos3 = s.find("*/");
                if (pos3 == string::npos) {
                    i++;
                    continue;
                }
                flag = 0;
                s = tmp + s.substr(pos3+2, s.size()-pos3-2);
            }
        }
        return ans;
    }
};

正则表达式法:

const regex re(R"((?:/\*[\s\S]*?\*/|//.*))");
class Solution {
public:
    vector<string> removeComments(vector<string>& source) {
        vector<string> ans;
        auto s = accumulate(source.cbegin(), source.cend(), ""s, [](const auto& a, const auto& b) { return a + b + '\n'; });
        for(istringstream ss(regex_replace(s, re, "")); getline(ss, s, '\n');)
            if (s.size()) ans.push_back(s);
        return ans;
    }
};

接下来逐行分析一下上述这段代码:
第一行声明了一个regex类型的常量re,其中R"(…)“: 是一个C++11引入的原始字符串字面量,它表示其中的字符串可以包含任意字符,包括转义字符,而无需对它们进行转义。
再解释一下正则模式的写法:
① (?: … ): 非捕获分组,表示这个分组仅用于组织正则表达式的结构,而不会捕获匹配的文本,不会分配额外的内存去存储,一定程度上性能更优。
② /\*[\s\S]*?\*/: 匹配多行注释。/\* 匹配 /*,[\s\S]*? 匹配任意字符(包括换行符),*? 表示非贪婪(懒惰)匹配(尽可能少地匹配字符),\*/ 匹配 */。
③ |: 或操作符,表示匹配左侧或右侧的模式。
④ //.*: 匹配单行注释。// 匹配 //,.* 匹配任意字符(除换行符外),直到行尾。
再来看一看第6行:
① accumulate函数:accumulate是numeric库中的一个函数,可以用来对指定范围内的元素求和,但也自定义其他操作。基本用法是:accumulate(起始迭代器, 结束迭代器, 初始值, 自定义操作函数)。这里我们设置从cbegin()到cend(),注:cbegin/cend相较于begin/end它们是const迭代器,不可修改。
② 初始值为”"s,这个写法也是字符串字面量,它代表创建的是std::string类型的对象而不是C风格的const char*类型。
③ 自定义操作函数,这里我们用到了C++的lambda表达式来定义一个匿名函数,用于用换行符去连接字符串。这里需要注意输入参数里的const标识,由于accumulate的执行过程中会产生临时对象,而C++不允许将临时对象绑定到非const引用上。
再看一看第7行:
使用regex_replace函数将s中与正则表达式re匹配的注释替换为空字符串。然后,使用istringstream将替换后的字符串转为输入流的形式。再使用getline函数逐行分割并读取字符串。这里很巧妙的一点是,getline写在for的第二个参数里,第三个参数置空,很好地利用了getline函数返回值的特点来简化代码。
最后的处理就不必多说了,判断一下是否为空再决定是否加入答案中就行了。
细节还是挺多的,虽然不是一道难题,但是值得一做。

关键词

力扣 每日一题 删除注释 722 模拟 字符串 正则表达式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值