括号匹配_进阶篇 ( 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把回车看作\0,而我们逐行读入,于是有以下条件
	if (a[0] == '.'&&a[1] == '\0') {
		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把回车看作\0,而我们逐行读入,于是有以下条件
	if (a[0] == '.'&&a[1] == '\0') {
		result = true;
	}
	return result;
}
posted @ 2019-04-22 20:16  BlackDn  阅读(1096)  评论(0编辑  收藏  举报
https://blog-static.cnblogs.com/files/luoyang0515/live2d.js https://files.cnblogs.com/files/kousak/waifu.css
Live2D