• 括号匹配_进阶篇 ( 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;
    }
    
  • 相关阅读:
    2016"百度之星"
    ZOJ 3703 Happy Programming Contest(01背包的灵活运用)
    LA 3942 Remember the Word (Trie树)
    ZOJ 3700 Ever Dream(Vector)
    Hdoj 1686 Oulipo
    2017总结,2018计划
    Ubuntu16.04 + caffe-ssd + [CPU_ONLY] + KITTI 训练总结
    【转载】The Elements of Programming Style之代码风格金科玉律
    qt中setStyleSheet导致的内存泄漏
    【转】用枚举定义有意义的数组下标
  • 原文地址:https://www.cnblogs.com/luoyang0515/p/10752693.html
Copyright © 2020-2023  润新知