表达式相关(一)操作数栈、运算符栈
NOIP2013普及组T2
只有加法和乘法的表达式
思考:
使用tok来存放操作数或操作符(在编译器词法分析中称之为token,故简写为tok);输入只有一行可以用fgets,不知道题目给的输入文件有没有换行(fgets是会读入换行符的),所以还要加个判断,不然存放的时候会把换行符也当做运算符
对于2+3+...的表达式,读到2+3之后可以直接先算出5,结果是与5+...等价的;然而读到2+3...的表达式,需要先计算3...后面的内容,因为乘法的优先级比加法要高;故只读到2+3的时候不能先做计算,反之读到2*3可以先算。
每次读到操作数肯定要进栈;读到操作符,则根据优先级进行判断和计算;因此需要两个栈
一个简单的便于理解的小图示
大致推断出过程:
解析token
若为操作数则入栈
若为运算符:
若运算符栈为空,则将当前运算符入栈
若当前运算符优先级高于栈顶运算符,则将当前运算符入栈
否则不断循环,直至运算符栈为空或者当前运算符优先级高于栈顶运算符:
栈顶两个操作数出栈
栈顶运算符出栈
将两个操作数进行计算,再进栈
循环结束,将当前运算符入栈
实际写代码时考虑到判断运算符栈为空比较困难,需要一个哨兵;
大致长这样,关于括号的事情后面再写
#include<bits/stdc++.h>
using namespace std;
char tok[100000][20];
int tok_max=0;
char s[500000];
int calc(char op,int num1,int num2){
assert(op=='+'||op=='*');
if(op=='+')return num1+num2;
if(op=='*')return num1*num2;
}
int priority(char op){
if(op=='*')return 2;
if(op=='+')return 1;
}
int main(){
fgets(s,sizeof(s),stdin);
int num=0;
if(s[strlen(s)-1]=='\n'){
s[strlen(s)-1]=0;
}
for(int i=0;i<strlen(s);i++){
if(isdigit(s[i])){
num=num*10+s[i]-'0';
}
else{
++tok_max;++tok_max;
sprintf(tok[tok_max-1],"%d",num);
*tok[tok_max]=s[i];
num=0;
}
}
++tok_max;
sprintf(tok[tok_max],"%d",num);
//printf("tok_max..%d\n",tok_max);
//for(int i=1;i<=tok_max;i++){
// puts(tok[i]);
//}
stack<int>optr,opnd;
//运算符栈、操作数栈
optr.push('#');//检验运算符栈非空的哨兵
for(int i=1;i<=tok_max;i++){
if(isdigit(*tok[i])){
int num1;
sscanf(tok[i],"%d",&num1);
opnd.push(num1);
}
else{
for(;optr.top()!='#' && priority(*tok[i]) <= priority(optr.top());){
int num2=opnd.top();opnd.pop();
int num3=opnd.top();opnd.pop();
char op=optr.top();optr.pop();
opnd.push(calc(op,num3,num2)%10000);
}
optr.push(*tok[i]);
}
}
//printf("%d\n",opnd.top());
for(;optr.top()!='#';){
int num2=opnd.top();opnd.pop();
int num3=opnd.top();opnd.pop();
char op=optr.top();optr.pop();
opnd.push(calc(op,num3,num2)%10000);
}
printf("%d\n",opnd.top()%10000);
return 0;
}