括号匹配_进阶篇 ( 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( )先....
最后纠结特殊括号。我们一个个情况来
- 首先是有了特殊左括号,右边不匹配,这时出栈的是星号,赋值给error。如果到了特殊右括号,但是此时栈空,那么赋给error的也是星号,所以如果error是星号还要进行额外判断。因为如果是右边不匹配,星号出栈,栈里还留着一个斜杠,所以栈肯定不为空。
- 其次是有了特殊右括号,但是和出栈的左括号不匹配,所以缺的是和出栈左括号相对应的右括号
//特殊括号/*的匹配,
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;
}