括号匹配_进阶篇 ( 7-2 符号配对 )

Posted luoyang0515

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了括号匹配_进阶篇 ( 7-2 符号配对 )相关的知识,希望对你有一定的参考价值。

括号匹配_进阶篇(/.../)

之前有个简单的括号匹配,令这三对括号进行匹配:( ),[ ],{ }
点击跳转:简单的括号匹配问题
之所以说他们简单,是因为每个括号都只占一个字符。
而进阶篇,虽然说起来很酷,其实就是再多一个对/* */的判断
先上原题

7-2 符号配对 (20 分)
请编写程序检查C语言源程序中下列符号是否配对:/*与*/、(与)、[与]、{与}。
输入格式:
输入为一个C语言源程序。当读到某一行中只有一个句点.和一个回车的时候,标志着输入结束。程序中需要检查配对的符号不超过100个。
输出格式:
首先,如果所有符号配对正确,则在第一行中输出YES,否则输出NO。然后在第二行中指出第一个不配对的符号:如果缺少左符号,则输出?-右符号;如果缺少右符号,则输出左符号-?。
输入样例1:
void test()
{
    int i, A[10];
    for (i=0; i<10; i++) /*/
        A[i] = i;
}
.


输出样例1:
NO
/*-?

输入样例2:
void test()
{
    int i, A[10];
    for (i=0; i<10; i++) /**/
        A[i] = i;
}]
.

输出样例2:
NO
?-]
输入样例3:
void test()
{
    int i
    double A[10];
    for (i=0; i<10; i++) /**/
        A[i] = 0.1*i;
}
.

输出样例3:
YES

我们可以套用上面简单括号匹配问题的代码,在其基础上修改。
思路还是一样,如果是左括号就入占,如果是右括号就出栈。
由于需要对两个符号都进行判断,只有同时成立才能判断为左括号,所以写出以下接口

//判断字符数组的第i个和第i+1个是不是/*
bool isAsterisk(char a[],int i) {
    bool result = false;
    if (a[i] == '/'&&a[i + 1] == '*') {
        result = true;
    }
    return result;
}

同理,有判断右括号

//判断字符数组的第i个和第i+1个是不是*/
bool isOtherAsterisk(char a[], int i) {
    bool result = false;
    if (a[i] == '*'&&a[i + 1] == '/') {
        result = true;
    }
    return result;
}

再将其放入主函数的遍历循环中,通过判断是否为左括号决定入栈

    if (isAsterisk(a, i)) {     //如果是/*就把两个入栈,同时i额外+1,避免/*/错误
            Push(stack, a[i]);
            Push(stack, a[i + 1]);
            i++;
        }

这里要注意i要额外加1。如果确定是左括号那么星号就相当于/的一部分,不需要再对它进行判断。
如果为右括号,就把这个右括号和栈顶进行比较,如果不匹配,就改变result的值。注意要出栈两次。

        else if (isOtherAsterisk(a, i)) {       //如果是*/就出栈比较
            temp = Pop(stack);
            if (!(a[i] == temp)) {
                result = 0;
                break;
            }
            temp = Pop(stack);
            if (!(a[i + 1] == temp)) {
                result = 0;
                break;
            }
        }

至此可以对含有特殊括号的语句进行基本的yes和no的判断。
然后是对读入和输出的修改。
首先看读入,不同于之前的一行,这次是一段代码,不仅有空格,还有回车。直到‘.’结束。
看到如此明显的结束条件,自然用循环,但是用什么函数读入呢?不理会回车,因为回车可以用循环来实现,这就变成了逐行读入,直到‘.’。空格是无论如何都避免不了的,所以一想得到要读入空格,不如就试试gets( )函数。
首先根据gets( )函数与我们的结束方式写一个判断是否结束的接口

//判断是否输入结束
bool isEnd(char a[]) {
    bool result = false;
    //“.回车”输入结束,gets把回车看作,而我们逐行读入,于是有以下条件
    if (a[0] == '.'&&a[1] == '') {
        result = true;
    }
    return result;
}

然后在把之前左括号入栈,右括号出栈比较的代码放入一个逐行读入的循环中

    while (gets_s(a)&&!isEnd(a)) {      //逐行读入
        length = strlen(a);
        for (int i = 0; i <= length; i++) {
            //一般括号的匹配
            if (a[i] == '(' || a[i] == '[' || a[i] == '{') {        //左括号入栈
                Push(stack, a[i]);
            }
            else if (a[i] == ')' || a[i] == ']' || a[i] == '}') {   //右括号,将其与出栈的字符尝试匹配
                temp = Pop(stack);
                if (!((a[i] == ')'&&temp == '(') || (a[i] == ']'&&temp == '[') || (a[i] == '}'&&temp == '{'))) {
                    result = 0;         //如果不匹配就将result赋0
                    break;
                }
            }
            //特殊括号/*的匹配,
            if (isAsterisk(a, i)) {     //如果是/*就把两个入栈,同时i额外+1,避免/*/错误
                Push(stack, a[i]);
                Push(stack, a[i + 1]);
                i++;
            }
            else if (isOtherAsterisk(a, i)) {       //如果是*/就出栈比较
                temp = Pop(stack);
                if (!(a[i] == temp)) {
                    result = 0;
                    break;
                }
                temp = Pop(stack);
                if (!(a[i + 1] == temp)) {
                    result = 0;
                    break;
                }
            }
        }
    }
    if (result == 1 && stack.base == stack.top) {       //匹配一定栈空,排除无右括号匹配,只有左括号
        cout << "YES" << endl;
    }
    else {
        cout << "NO" << endl;
    }
    return 0;
}

以上就可以在题目的输入条件下输出YES或者NO了,但是还却,如果是NO,需要输出缺失的括号。
上述代码可以看出,当括号不匹配的时候,temp是不变的,仍是上一个出栈的元素。当temp和当前右括号匹配不上的时候,则缺失与temp对应的右括号。什么时候缺左括号呢?马上想到的是栈为空的时候,此时来了个右括号,那肯定是缺左括号了。先根据以上想法写一下,当然是不考虑特殊括号的。(多加的判断栈是否为空的函数在此不给出具体内容)

int main() {
    char a[1000] = { ' ' }; //a用来读取数据,legnth为长度
    int length = 0;     //遍历a,遇到左括号,入栈
    char temp = ' ';    //temp接收出栈的值
    int result = 1;     //result用于记录是否匹配,匹配为1,不匹配为0。一开始为0,不用讨论不存在括号的情况。
    char error = ' ';   //用来表示缺的括号是和error对应的括号
    SqStack stack;
    InitStack(stack);
    //cin.getline(a, 100);
    
    while (gets_s(a)&&!isEnd(a)) {      //逐行读入
        length = strlen(a);
        for (int i = 0; i <= length; i++) {
            //一般括号的匹配
            if (a[i] == '(' || a[i] == '[' || a[i] == '{') {        //左括号入栈
                Push(stack, a[i]);
            }
            else if (a[i] == ')' || a[i] == ']' || a[i] == '}') {   //右括号,将其与出栈的字符尝试匹配
                if (isEmpyt(stack)) {   //为空就说明缺左括号并且肯定不匹配,退出循环
                    error = a[i];
                    result = 0;
                    break;
                }
                else {
                    temp = Pop(stack);
                    if (!((a[i] == ')'&&temp == '(') || (a[i] == ']'&&temp == '[') || (a[i] == '}'&&temp == '{'))) {
                        result = 0;         //如果不匹配就将result赋0,且error为出栈的左括号,缺了右括号
                        error = temp;
                        break;
                    }
                }
            }

            //特殊括号/*的匹配
            略....
            
        }
    }
    if (result == 1 && isEmpyt(stack)) {        //匹配一定栈空,排除无右括号匹配,只有左括号
        cout << "YES" << endl;
    }
    else {
        cout << "NO" << endl;
        switch (error) {
        case '(':
            cout << "(-?";
            break;
        case '[':
            cout << "[-?";
            break;
        case '{':
            cout << "{-?";
            break;
        case ')':
            cout << "?-)";
            break;
        case ']':
            cout << "?-]";
            break;
        case '}':
            cout << "?-}";
            break;
        }
    }
    return 0;
}

这里插入一个小问题。我试着在PTA提交当前数据,结果总是编译错误。一查发现PTA已经不支持gets( )函数了...晕死,于是把gets( )改成cin.getline( )先....
最后纠结特殊括号。我们一个个情况来

  1. 首先是有了特殊左括号,右边不匹配,这时出栈的是星号,赋值给error。如果到了特殊右括号,但是此时栈空,那么赋给error的也是星号,所以如果error是星号还要进行额外判断。因为如果是右边不匹配,星号出栈,栈里还留着一个斜杠,所以栈肯定不为空。
  2. 其次是有了特殊右括号,但是和出栈的左括号不匹配,所以缺的是和出栈左括号相对应的右括号
                //特殊括号/*的匹配,
            if (isAsterisk(a, i)) {     //如果是/*就把两个入栈,同时i额外+1,避免/*/错误
                Push(stack, a[i]);
                Push(stack, a[i + 1]);
                i++;
            }
            else if (isOtherAsterisk(a, i)) {       //如果是*/就出栈比较
                if (isEmpyt(stack)) {   //为空就说明缺左括号并且肯定不匹配,退出循环
                    error = a[i];
                    result = 0;
                    break;
                }
                else {      //遇到特殊右括号且栈不为空,出栈比较。不符合就说明缺了与出栈括号相对的右括号
                    temp = Pop(stack);
                    if (!(a[i] == temp)) {
                        error = temp;
                        result = 0;
                        break;
                    }
                    temp = Pop(stack);
                    if (!(a[i + 1] == temp)) {
                        error = temp;
                        result = 0;
                        break;
                    }
                }
            }
                .......
                case '*':
            if (!isEmpyt(stack)) {    //栈非空缺右括号
                cout << "/*-?";
            }
            else {
                cout << "-?*/";        //栈空缺左括号
            }
            break;
            .......

最后仍然是一个特殊情况://
因为如果只扫到斜杠,并不会把它认为是右括号而扫入,那么在这种情况下,栈非空,且error没有被赋值。
也就是说,最后的特殊情况是:只有左括号而没有右括号
于是我们要做的就是先出栈,然后先输出出栈的符号再输出”-?“,因为此时肯定是缺了右括号
注意特殊符号,可以不用出栈而直接用“/
-?”,不然它只会输出星号
最后要注意的一点是,我们要将匹配的特殊右括号出栈后,令i++,否则程序下一步会对特殊右括号的斜杠进行判断(认为是特殊左括号的斜杠)

#include <iostream>
#include <string.h>

using namespace std;

typedef struct {
    char *base;     //栈底指针
    char *top;      //栈顶指针
    int stacksize;  //最大容量
}SqStack;

void InitStack(SqStack &S);
void Push(SqStack &S, char e);
char Pop(SqStack &S);
bool isEmpyt(SqStack S);
bool isAsterisk(char a[], int i);
bool isOtherAsterisk(char a[], int i);
bool isEnd(char a[]);

int main() {
    char a[1000] = { ' ' }; //a用来读取数据,legnth为长度
    int length = 0;     //遍历a,遇到左括号,入栈
    char temp = ' ';    //temp接收出栈的值
    int result = 1;     //result用于记录是否匹配,匹配为1,不匹配为0。一开始为0,不用讨论不存在括号的情况。
    char error = ' ';   //用来表示缺的括号是和error对应的括号
    SqStack stack;
    InitStack(stack);
    while (cin.getline(a,1000)&&!isEnd(a)) {        //逐行读入
        length = strlen(a);
        for (int i = 0; i <= length; i++) {
            //一般括号的匹配
            if (a[i] == '(' || a[i] == '[' || a[i] == '{') {        //左括号入栈
                Push(stack, a[i]);
            }
            else if (a[i] == ')' || a[i] == ']' || a[i] == '}') {   //右括号,将其与出栈的字符尝试匹配
                if (isEmpyt(stack)) {   //为空就说明缺左括号并且肯定不匹配,退出循环
                    error = a[i];
                    result = 0;
                    break;
                }
                else {
                    temp = Pop(stack);
                    if (!((a[i] == ')'&&temp == '(') || (a[i] == ']'&&temp == '[') || (a[i] == '}'&&temp == '{'))) {
                        result = 0;         //如果不匹配就将result赋0,且error为出栈的左括号,缺了右括号
                        error = temp;
                        break;
                    }
                }
            }
            //特殊括号/*的匹配,
            if (isAsterisk(a, i)) {     //如果是/*就把两个入栈,同时i额外+1,避免/*/错误
                Push(stack, a[i]);
                Push(stack, a[i + 1]);
                i++;
            }
            else if (isOtherAsterisk(a, i)) {       //如果是*/就出栈比较
                if (isEmpyt(stack)) {   //为空就说明缺左括号并且肯定不匹配,退出循环
                    error = a[i];
                    result = 0;
                    break;
                }
                else {      //遇到特殊右括号且栈不为空,出栈比较。不符合就说明缺了与出栈括号相对的右括号
                    temp = Pop(stack);
                    if (!(a[i] == temp)) {
                        error = temp;
                        result = 0;
                        break;
                    }
                    temp = Pop(stack);
                    if (!(a[i + 1] == temp)) {
                        error = temp;
                        result = 0;
                        break;
                    }
                    i++;    //如果/* */配对,则要跳过右括号的斜杠
                }
            }
        }
    }
    if (result == 1 && isEmpyt(stack)) {        //匹配一定栈空,排除无右括号匹配,只有左括号
        cout << "YES" ;
    }
    else {        //这里进行了优化
        cout << "NO" << endl;
        switch (error) {
        case '(':
        case '[':
        case '{':
            cout << error<< "-?";
            break;
        case ')':
        case ']':
        case '}':
            cout << "?-" <<error;
            break;
        case '*':
            if (!isEmpyt(stack)) {  //栈非空缺右括号
                cout << "/*-?";
            }
            else {                  //栈空缺左括号
                cout << "?-*/";
            }
            break;
        case ' ':
            temp = Pop(stack);
            if (temp == '*') {
                cout <<  "/*-?";
            }
            else {
                cout << temp << "-?";
            }
            break;
        default:
            break;
        }
    }
    return 0;
}

//初始化分配空间,top=base,表示空栈
void InitStack(SqStack &S) {
    S.base = new char[102];     //最大容量
    S.top = S.base;
    S.stacksize = 102;
    //cout << "初始化成功" << endl;
}

//将e入栈,栈顶指针+1
void Push(SqStack &S, char e) {
    *S.top = e;
    S.top++;
    //cout << "入栈成功" << endl;
}

//出栈,赋值给e并返回
char Pop(SqStack &S) {
    char e;
    S.top--;
    e = *S.top;
    return e;
}

//判断栈空
bool isEmpyt(SqStack S) {
    bool result = false;
    if (S.top == S.base) {
        result = true;
    }
    return result;
}

//判断字符数组的第i个和第i+1个是不是/*
bool isAsterisk(char a[],int i) {
    bool result = false;
    if (a[i] == '/'&&a[i + 1] == '*') {
        result = true;
    }
    return result;
}

//判断字符数组的第i个和第i+1个是不是*/
bool isOtherAsterisk(char a[], int i) {
    bool result = false;
    if (a[i] == '*'&&a[i + 1] == '/') {
        result = true;
    }
    return result;
}

//判断是否输入结束
bool isEnd(char a[]) {
    bool result = false;
    //“.回车”输入结束,gets把回车看作,而我们逐行读入,于是有以下条件
    if (a[0] == '.'&&a[1] == '') {
        result = true;
    }
    return result;
}

以上是关于括号匹配_进阶篇 ( 7-2 符号配对 )的主要内容,如果未能解决你的问题,请参考以下文章

如何使用栈来实现括号匹配

PHP:如何匹配一系列 unicode 配对代理表情符号/表情符号?

7-2 符号配对 (20 分)

设计一个判别表达式中左、右括号是不是配对出现的算法,采用啥数据结构最佳。

表达式括号匹配配对判断问题

第03次作业-栈和队列