【剑指Offer记录】20_表示数值的字符串

Part1. 我的思路和代码(通过92.31%)

这道题我并没有自己做出来,而是先看了书上的前一道题(也是正则表达式匹配),然后再做,而且做的时候依然是跟着书上的思路才基本做出,最后对空格的小处理为自己的思路(如Part2所述)。

整体思路上,先扫描小数点前的有符号整数部分,扫描完后若接下来为小数点,则再扫描一个无符号整数,扫描完后若接下来为e/E,则再扫描一个有符号整数。

代码中处理小数点的分支,使用了或逻辑,这包括4种情况:(1)小数点前没有整数部分,ans先为false,进一步扫描后小数点后也没有整数,扫描结果为false,或运算后为false,也就是最终结果,如".“;(2)小数点前没有整数部分,ans先为false,进一步扫描后小数点后有整数,扫描结果为true,或运算后为true,比如”.123";(3)小数点前有整数部分,ans先为true,进一步扫描后小数点没有整数,扫描结果为false,或运算后为true,比如"123.“;(4)小数点前有整数部分,ans先为true,进一步扫描后小数点后有整数,扫描结果为true,或运算后为true,比如"123.456”。

代码中处理e/E的分支,使用了与逻辑,包括3种情况:(1)之前ans为false,说明e之前没有数字,无论e之后如何,最终结果均为false,比如"e789"、“e”;(2)之前ans为true,说明e之前有数字,进一步扫描后结果为false,即e之后没有数字,与运算结果为false,比如"123.456e";(3)之前ans为true,说明e之前有数字,进一步扫描后结果为true,即e之后有数字,与运算结果为true,比如"123.456e789"。

代码行数不多,但包含的情况非常多……

bool scanUnsignedInt(int &idx, const string &str)
{
    int beforeIdx = idx;

    while(idx<str.length() && '0'<=str[idx] && str[idx]<='9'){
        ++idx;
    }

    return idx>beforeIdx;
}

bool scanInt(int &idx, const string &str)
{
    if(str[idx]=='+' || str[idx]=='-'){
        ++idx;
    }
    return scanUnsignedInt(idx, str);
}

bool isNumeric(string str)
{
    bool ans;
    int idx;

    if(str.length() == 0){
        return false;
    }
    idx = 0;
    ans = scanInt(idx, str);
    if(str[idx] == '.'){
        ++idx;
        ans = scanUnsignedInt(idx, str) || ans; //注意由于短路逻辑,ans不能写到前面,否则ans为true时,不会进入函数执行,也就不会更新idx,导致之后的匹配过程出错
    }
    if(str[idx]=='e' || str[idx]=='E'){
        ++idx;
        ans = scanInt(idx, str) && ans;
    }

    return ans && (idx==str.length());
}

特别记录:需要注意短路逻辑,对于||逻辑,执行到为true的条件就不继续向后执行了,对于&&逻辑,执行到为false的条件就不继续向后执行了。

Part2. 我的思路和代码(通过100%)

我做题的平台是牛客网,该平台此题目和书上不同的地方主要在于存在空格。若不处理空格,则无法通过所有用例,若考虑空格,空格可能出现的位置如下图所示:

在这里插入图片描述

在科学计数法中,空格可能出现在e之前的整数或小数部分和e之后的整数部分(首尾部分的蓝色和中间的红色)、整个数的首尾(绿色),如图中举的例子。在经过长时间的思考后,打算目前放弃较为完美的解题,只先处理整个数首、尾部分的空格,但最终通过了所有用例。具体做法是用循环,分别从首、尾向后、向前扫描字符串,扫描到非空格字符为止。

bool scanUnsignedInt(int &idx, const string &str)
{
    int beforeIdx = idx;

    while(idx<str.length() && '0'<=str[idx] && str[idx]<='9'){
        ++idx;
    }

    return idx>beforeIdx;
}

bool scanInt(int &idx, const string &str)
{
    if(str[idx]=='+' || str[idx]=='-'){
        ++idx;
    }
    return scanUnsignedInt(idx, str);
}

bool isNumeric(string str)
{
    bool ans;
    int idx;
    int idx_s;
    int idx_e;

    for(idx_s=0; idx_s<str.length(); idx_s++){ //处理str两端的空格
        if(str[idx_s] != ' '){
            break;
        }
    }
    for(idx_e=str.length()-1; idx_e>=0; idx_e--){
        if(str[idx_e] != ' '){
            break;
        }
    }
    str = std::string(str, idx_s, idx_e-idx_s+1);
    if(str.length() == 0){
        return false;
    }
    idx = 0;
    ans = scanInt(idx, str);
    if(str[idx] == '.'){
        ++idx;
        ans = scanUnsignedInt(idx, str) || ans; //注意由于短路逻辑,ans不能写到前面,否则ans为true时,不会进入函数执行,也就不会更新idx,导致之后的匹配过程出错
    }
    if(str[idx]=='e' || str[idx]=='E'){
        ++idx;
        ans = scanInt(idx, str) && ans;
    }

    return ans && (idx==str.length());
}

Part3. 心得体会

  • 通过这道题,我深刻感受到了字符串处理正则表达式匹配类型题目的难度,做这道题之前,首先看了书上一道相似的正则表达式题目,但该题的情况更加复杂。虽然不是完全由自己做出来,但也是一种经验的积累;
  • 要注意语言特性的影响,例如短路逻辑;
  • 努力的同时需要注意全局,这道题做了好几天还没有做出,对我而言说明要寻求答案帮助,作为经验积累下来,不能因此独自死磕。
  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值