7-8 符号配对 (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
【思路】
这道题考试的时候也是死磕了一下,毕竟分值很大,不想丢掉,但磕到最后,也只是悲惨的水到了4分左右的样子,不仅如此,还耗费了大量时间。这次要引以为戒,天梯赛上遇到难磕的题千万不能陷死在里面了。。。
其实这题并不难,基本思路就是利用栈,如果字符和栈顶的字符匹配,那么我就把栈顶元素弹出来。如果不匹配,我就压进去。我当时只想到这一步,然后就开始信心满满的做,做到最后提交答案错误和段错误。。。然后就没法继续了。。
题目不难,但这题AC率是最低的,当时考试的时候只有0.06.。。我觉得,这道题真正难的地方不是思路,而是合理设计代码,然后处理大量的细节,这个真的很繁琐
我参考了这位博主的代码,他的代码设计的很好:https://blog.csdn.net/qq_41611106/article/details/82590671
总结一下我为什么实现出来就乱七八糟,他实现出来就显得非常有条理:
1.我用的string,他用的char数组。其实这两个都无所谓,但是他用起来还是比我的string看着爽一些,这就是基本功的差距了。char数组可以将字符串末尾的\0读进来,用scanf("%s", str);不能读空格,他相当于没有管空格,因为空格在这里确实也不用管。而我用的是getline(cin, str),把空格也一口气的读进来了
2.他采用了的输出思路是,一旦找到一个不匹配的,就直接输出了,flag变为true。
而我的思路不是边读边输出,而是最后在总的输出。其实,这一点,我觉得他的设计就比我更好了,这样不至于在最后考虑太多种情况,而且提高了速度,只要找到一个不匹配,后面的就不用再找了。
3.对于注释符号/*我已开始是不知道怎么处理的,因为它由两个符号组成。但是他设计的就挺好,如果找到注释符号,那么就往栈中压入一个<,这样用一个符号就代替了两个符号了!
4.栈pop的首要条件是栈不为空,这点我没有考虑,于是最后也是出现大量段错误!
5.逻辑的问题我出现了错误。比如栈顶现在是[,然后来了一个},那么这个时候第一个不匹配的元素其实是栈顶的[,而不是}。我一开始以为是},这一点就想错了。
6、最后,其实如果遇到了( [ {
这种左括号的字符,我们直接压到栈中去就行了,匹配的是右括号。最后如果栈中还有字符,那么肯定是这种左括号字符,输出栈顶的那一个就可以了。这样思考问题就简单多了。
所以,我跟他的很多思考问题的方式还是很有差距的。这其实并不是算法了,感觉是一种处理问题的能力,这种能力要大量刷题训练才行,要让我们每次思考问题都能想到一种最简化的处理问题方式。
所以有时候,真不是算法题目太难,而是你思考的方向南辕北辙,你设计的数据结构不合理。
代码:
#include<iostream>
#include<stack>
using namespace std;
int main()
{
char str[102];
stack<char> s;
bool flag = false;
while(true)
{
scanf("%s", str);
if(str[0] == '.' && str[1] == '\0') break; //我们每次会读入一个\0字符进来
//开始遍历str字符数组
for(int i = 0;str[i] != '\0';i++)
{
if(flag) break; //如果找到一个,后面的字符串还得读取,但不需要判断了
if(str[i]=='(' || str[i] =='[' || str[i]=='{'){
s.push(str[i]);
}
//这样来判断注释符号!
else if(str[i] == '/' && str[i + 1] == '*'){
s.push('<'); //非常巧妙!用一个<代替注释符号!
i++; //后面那个字符就不用再看了
}
else if(str[i] == ')'){
//栈不为空是一个前提!否则一定段错误!
if(!s.empty() && s.top() == '(') s.pop();
else{
cout << "NO" << endl;
flag = true; //已经找到一个不匹配的了
//cout << "?-)" << endl;
//这里的逻辑问题注意一下
if(s.empty()){
cout << "?-)" << endl;
}
else//说明栈顶元素和)不匹配,那么第一个不配对的实际上是栈顶元素
{
if(s.top() == '<')
cout << "/*-?" << endl;
else
cout << s.top() << "-?" << endl;
}
break;
}
}
else if(str[i] == ']') {
if(!s.empty() && s.top() == '[') s.pop();
else {
printf("NO\n");
flag = true;
if(s.empty()) {
printf("?-]\n");
}
else {
if(s.top() == '<')
cout << "/*-?" << endl;
else
cout << s.top() << "-?" << endl;
}
break;
}
}
else if(str[i] == '}') {
if(!s.empty() && s.top() == '{') s.pop();
else {
printf("NO\n");
flag = true;
if(s.empty()) {
printf("?-}\n");
}
else {
if(s.top() == '<')
cout << "/*-?" << endl;
else
cout << s.top() << "-?" << endl;
}
break;
}
}
//如果是反注释符号
else if(str[i] == '*' && str[i + 1] == '/')
{
i++;
if(!s.empty() && s.top() == '<'){
s.pop();
}
else{
cout << "NO" << endl;
if(s.empty()){
cout << "?-*/" << endl;
}
else{ //栈顶元素不匹配
cout << s.top() << "-?" << endl;
}
flag = true;
break; //找到一个不匹配的字符,直接跳出即可
}
}
}
}
//注:此时还有一种情况,就是栈中都是左边字符,比如(,[,{,这样的话flag还是false
if(!flag && s.empty()) //如果栈是空的,才证明完全匹配了
cout << "YES" << endl;
else if(!flag){ //否则,里面肯定还有一系列的左边字符
cout << "NO" << endl; //这句话差点写掉了
if(s.top() == '<') //这句特判不要掉!
cout << "/*-?" << endl;
else
cout << s.top() << "-?" << endl;
}
return 0;
}