【模式匹配】正则表达式匹配,表示数值的字符串

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/82425798

面试题19:正则表达式匹配

请实现一个函数用来匹配包含’.’和’*’的正则表达式。模式中的字符’.’表示任意一个字符,而’*’表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但与”aa.a”及”ab*a”均不匹配。

基本可以看成编译原理的parser问题,但是找出文法不容易,也没必要为每种情况写个匹配的函数(递归下降),可以画个NFA分析一下,然后在递归里直接用字符串游标的移动记录匹配情况。

#include<bits/stdc++.h>
using namespace std;

bool matchCore(const char* str, const char* pattern);

// 参数:
//        str:       要匹配的字符串首地址 
//        pattern:   要匹配的模式串首地址 
// 返回值:
//        能否匹配成功 
bool match(const char* str, const char* pattern) {
    //非空校验 
    if(str == nullptr || pattern == nullptr)
        return false;
    //调用匹配的函数,递归地进行匹配 
    return matchCore(str, pattern);
}

// 参数:
//        str:       要匹配的字符(子)串首地址 
//        pattern:   要匹配的模式(子)串首地址 
// 返回值:
//        从此位置向后能否匹配成功 
bool matchCore(const char* str, const char* pattern) {
    if(*str == '\0' && *pattern == '\0')//如果两串都到达结尾 
        return true;//匹配成功 

    if(*str != '\0' && *pattern == '\0')//如果模式串先到达结尾 
        return false;//匹配一定失败

    //另:如果字符串先到达结尾,不一定匹配失败,因为可能模式串剩下'a*'或者'*'可以匹配空 

    //如果模式串下一字符是'*',那么会影响当前字符的匹配长度 
    if(*(pattern + 1) == '*') {
        //如果当前模式是和字符串匹配的,或者当前模式是'.'可匹配任一字符 
        if(*pattern == *str || (*pattern == '.' && *str != '\0'))//即如果当前是匹配的 
            //考虑三种匹配方式
            //跳过当前字符串上的'a',跳过模式串'a*',即'*'使'a'重复1次 
            return matchCore(str + 1, pattern + 2)
                   //跳过当前字符串上的'a',期望后面的'a'还能匹配'a*',即'*'使'a'重复>1次  
                   || matchCore(str + 1, pattern)
                   //跳过模式串'a*',即'*'使'a'重复0次
                   || matchCore(str, pattern + 2); 
        else//如果当前模式不能匹配字符串上的这个字符 
            //这时这个'*'号只能起到使前面的字符'a'重复0次的效果 
            return matchCore(str, pattern + 2);
    }

    //以下是后面没有跟'*'的情况 

    //如果当前模式匹配字符串该位置的字符('a'-'a'匹配或者'a'-'.'匹配) 
    if(*str == *pattern || (*pattern == '.' && *str != '\0'))
        return matchCore(str + 1, pattern + 1);//匹配,两个串都前进一步 

    //至此还没有返回,说明这个(子)匹配已经失败 
    return false;
}

int main(){
    cout<<boolalpha<<match("aaca","ab*a*c*a")<<endl;
    return 0;
}

面试题20:表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串“+100”、“5e2”、“-123”、“3.1416”及“-1E-16”都表示数值,但“12e”、“1a3.14”、“1.2.3”、“+-5”及“12e+5.4”都不是。

表示数值的字符串遵循模式A[.[B]][e|EC] | .B[e|EC]。其中A是数值的整数部分,B是数值的小数部分,C是数值的整数部分,eE是终结符表示带科学计数法(10的几次幂),AC能以终结符+-开头,也可以像B一样是普通的数位串。

#include<bits/stdc++.h>
using namespace std;

bool scanUnsignedInteger(const char** str);
bool scanInteger(const char** str);

//数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,其中A和C都是
//整数(可以有正负号,也可以没有),而B是一个无符号整数
bool isNumeric(const char* str) {
    //输入非空校验 
    if(str == nullptr)
        return false;
    //在开头尝试扫描一下有符号整数 
    //如果扫描到就扫完了A,接下来要遇到.B或者指数部分或者啥也没有 
    //如果没扫到,接下来就必须要遇到.B 
    bool numeric = scanInteger(&str);//在这个函数内会移动str(字符串的一级指针) 

    //如果出现'.',则接下来是数字的小数部分,即B部分 
    if(*str == '.') {
        ++str;//越过这个'.'

        //如果之前numeric为真,说明扫过了A,即便有.有没有B也无所谓,如'233.'即233.0合法 
        //没扫过A时,一定要扫'.'然后扫B部分,扫B部分用无符号整数,如'.233'即0.233合法 
        numeric = scanUnsignedInteger(&str) || numeric;
    }

    //如果没有扫到A也没有出现'.',这时候已经非法了
    //可以在这里直接返回,但后面的代码也适用,不会将false变成true
    //书上就是没有直接返回,接着用后面的代码,因为都是与运算,所以没关系 
    //总之,到现在为止如果numeric为true最后不一定true,但是为false最后一定false了 

    //所以不妨加个剪枝提高效率
    if(!numeric)
        return false;
    //注意!加了这个剪枝以后,后面实际上就没必要再和numeric做&&运算了,但这里还是保留书上的代码 

    //如果出现'e'或者'E',接下来跟着的是数字的指数部分
    if(*str == 'e' || *str == 'E') {
        ++str;//越过这个'e'或者'E' 

        //下面一行代码用&&的原因:
        //当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
        //当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4
        numeric = numeric && scanInteger(&str);
    }
    //当然也可以不出现指数部分,那么后面就要直接结束了

    //结束时还要判断一下已经走到了字符串结尾,而不会有遗留的未匹配到的部分 
    return numeric && *str == '\0';
}

//扫描无符号整数,传入一个字符串位置的二级指针,扫描成功返回true
//(对于B直接调用它,因为B不能带符号)
bool scanUnsignedInteger(const char** str) {
    const char* before = *str;//将当前扫描的起始地址记录下来
    //只要没到字符串末尾,而且出现的都是'0'~'9'之间的字符 
    while(**str != '\0' && **str >= '0' && **str <= '9')
        ++(*str);//就一直向下扫描 
    return *str > before;//前面的循环至少进行一次才会大于,即整数必须存在
    //那么对于可以不存在这种情况(如A),应由函数的调用方来维护 
}

//扫描整数,传入一个字符串位置的二级指针,扫描成功返回true
//(对于A和C调用它,因为A和C是可以带符号的) 
bool scanInteger(const char** str) {
    if(**str == '+' || **str == '-')//如果以'+'或者'-'号开头
        ++(*str);//让其一级指针+1以跳过这个符号,它是允许出现在头部的符号,也可以不出现 
    //此时这个二级指针指向的是那个+1后的一级指针了 
    return scanUnsignedInteger(str);//调用扫描无符号整数的方法,还是传入这个二级指针就行 
}

int main(){
    cout<<boolalpha<<isNumeric("1.79769313486232E+308")<<endl;
    cout<<boolalpha<<isNumeric(".e1")<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/82425798