Complicated Expressions
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 887 | Accepted: 314 |
Description
The most important activity of ACM is the GSM network. As the mobile phone operator, ACM must build its own transmitting stations. It is very important to compute the exact behaviour of electro-magnetic waves. Unfortunately, prediction of electro-magnetic fields is a very complex task and the formulas describing them are very long and hard-to-read. For example, below are the Maxwell's Equations describing the basic laws of electrical engineering.
(公式略)
ACM has designed its own computer system that can make some field computations and produce results in the form of mathematic expressions. Unfortunately, by generating the expression in several steps, there are always some unneeded parentheses inside the expression. Your task is to take these partial results and make them "nice" by removing all unnecessary parentheses.
Input
There is a single positive integer T on the first line of input. It stands for the number of expressions to follow. Each expression consists of a single line containing only lowercase letters, operators (+, -, *, /) and parentheses (( and )). The letters are variables that can have any value, operators and parentheses have their usual meaning. Multiplication and division have higher priority then subtraction and addition. All operations with the same priority are computed from left to right (operators are left-associative(左结合)). There are no spaces inside the expressions. No input line contains more than 250 characters.
Output
Print a single line for every expression. The line must contain the same expression with unneeded parentheses removed. You must remove as many parentheses as possible without changing the semantics of the expression. The semantics of the expression is considered the same if and only if any of the following conditions hold:
- The ordering of operations remains the same. That means "(a+b)+c" is the same as "a+b+c", and "a+(b/c)" is the same as "a+b/c".
- The order of some operations is swapped but the result remains unchanged with respect to the addition and multiplication associativity. That means "a+(b+c)" and "(a+b)+c" are the same. We can also combine addition with subtraction and multiplication with division, if the subtraction or division is the second operation. For example, "a+(b-c)" is the same as "a+b-c".
You cannot use any other laws, namely you cannot swap left and right operands and you cannot replace "a-(b-c)" with "a-b+c".
Sample Input
8
(a+(b*c))
((a+b)*c)
(a*(b*c))
(a*(b/c)*d)
((a/(b/c))/d)
((x))
(a+b)-(c-d)-(e/f)
(a+b)+(c-d)-(e+f)
Sample Output
a+b*c
(a+b)*c
a*b*c
a*b/c*d
a/(b/c)/d
x
a+b-(c-d)-e/f
a+b+c-d-(e+f)
Source
先读懂题意:
题目是要在不改变原来中缀表达式的语义的前提下,将输入的中缀表达式中多余的圆括号去掉。
基本知识:
1)把中缀表达式转换为后缀表达式算法的基本思路是从头到尾地扫描中缀表达式中的每个字符,对于不同类型的字符按不同情况进行处理。
设输入的中缀表达式是s1,转换后的结果字符串是s2
加减运算符的优先级设定为1,乘除运算符的优先级设定为2,在栈中保存的特殊运算符’@’(中缀表达式结束符)和’(’的优先级设定为0 。
1. 若遇到的是空格则认为是分隔符,不需要进行处理;
2. 若遇到的是数字或小数点,则直接写入到s2中,并在每个数值的最后写入一个空格;
3. 若遇到的是左括号,则应把它压入到运算符栈中,待以它开始的括号内的表达式转换完毕后再出栈;
4. 若遇到的是右括号,则表明括号内的中缀表达式已经扫描完毕,把从栈顶直到保存着的对应左括号之间的运算符依次退栈并写入s2串中;
5. 若遇到的是运算符:
5.1 当该运算符的优先级大于栈顶运算符的优先级时,表明该运算符的后一个运算对象还没有被扫描也没有被放入到s2串中,应把它暂存于运算符栈中,待它的后一个运算对象从s1串中读出并写入到s2串中后,再令其出栈并写入s2串中;
5.2 若遇到的运算符的优先级小于或等于栈顶运算符的优先级,这表明栈顶运算符的两个运算对象已经被保存到s2串中,应将栈顶运算符退栈并写入到s2串中,对于新的栈顶运算符仍继续进行比较和处理,直到被处理的运算符的优先级大于栈顶运算符的优先级为止,然后让该运算符进栈即可。
按照以上过程扫描到中缀表达式结束符’@’时,把栈中剩余的运算符依次退栈并写入到后缀表达式中,再向s2写入表达式结束符’@’和字符串结束符’{post.abstract}’,整个转换过程就处理完毕,在s2中就得到了转换成的后缀表达式。
后缀表达式转中缀表达式的基本思路是从头到尾地扫描后缀表达式中的每个字符,对于不同类型的字符按不同情况进行处理。
设输入的中缀表达式是s1,转换后的结果字符串是s2,用于存储中间操作符和操作数的堆栈。
加减运算符的优先级设定为1,乘除运算符的优先级设定为2,操作数的优先级设定为0 。
(注意:输入是后缀表达式,因此不可能含有空格或者圆括号等)。
1.遇到操作数,直接进堆栈;
2.遇到操作符,此处操作符假定都是双目的(+ - * /),则:
2.1如果最后一个进栈(即当前栈顶元素)的是操作符,且1/它的优先级低于当前输入字符;或者2/当优先级相同,且当前输入字符是’/’或’-’时,将栈顶元素弹出并在两边添加圆括号之后,存入临时变量tmp2中;否则直接将栈顶元素存入临时变量tmp2中。
2.2弹出当前栈顶元素(是2.1中栈顶元素的下面一个),若该元素是操作符,且优先级小于当前输入的字符,则添加圆括号,并存入临时变量tmp1中;否则,直接将栈顶元素存入临时变量tmp1中。
2.3将tmp1+当前输入字符+tmp2拼接在一起,并存入堆栈中。
解题思路:
先由中缀表达式得到后缀表达式,此时后缀表达式中去掉了所有的圆括号,接着再由后缀表达式恢复得中缀表达式,此时中缀表达式中圆括号最少。
代码如下:
#include <iostream>
#include <string>
std::string str;
char out[251]; //存储操作数和运算结果的堆栈
int pout; //指向out堆栈的栈顶指针
//中缀表达式转换为后缀表达式,并存于结果字符串out中
void infix2suffix(std::string s)
{
char tmp[251];//临时堆栈,用于存放操作符(不含操作数)
int ptmp = 0; //临时堆栈的栈顶指针
for(int i=0; i<s.length(); i++)
{
switch(s[i])
{
case '(': //是开括号,直接进栈
tmp[ptmp++] = s[i];
break;
case '+': //'+'和'-'优先级最低,在遇到开括号之前,循环执行
case '-': //弹出堆栈tmp中刚才压入的字符(因为优先级低的在栈底,高的在栈顶)
while(ptmp && tmp[ptmp-1] != '(' )
{
out[pout++] = tmp[--ptmp];
}
tmp[ptmp++] = s[i]; //最后把这个字符压入堆栈tmp
break;
case '*': //'*'和'/'不是最低优先级,在遇到开括号'('、优先级较低的'+'和'-'
case '/': //之前,循环执行弹出堆栈tmp刚才压入的字符
//while(ptmp!=0 && tmp[ptmp-1]!='(' && tmp[ptmp-1]!= '+' && //tmp[ptmp-1]!= '-')与下面while等效
while(ptmp && (tmp[ptmp-1]== '*' || tmp[ptmp-1] == '/'))
{
out[pout++] = tmp[--ptmp];
}
tmp[ptmp++] = s[i]; //最后把这个字符压入堆栈tmp
break;
case ')': //是闭括号,则在遇到开括号'('之前,循环执行弹出堆栈tmp中的字符
//并把它们添加到结果字符串堆栈out中
while(ptmp && tmp[--ptmp]!='(' )
{
out[pout++] = tmp[ptmp];
}
break;
default: //是操作数(注意,本题输入不含空格,否则需另外处理)
//直接将它添加到结果字符串堆栈中
out[pout++] = s[i];
break;
}
}
//上面循环完,说明到达了输入字符串的尾部了,此时
//循环弹出tmp中剩余的字符并添加到结果字符串堆栈out中
while(ptmp)
{
out[pout++] = tmp[--ptmp];
}
out[pout] = '/0'; //设置字符串结束标识
}
//后缀表达式转换为中缀表达式out
void suffix2infix(char *s)
{
char stack[251][251];
int priority[251];
int pstack = 0;
int len = strlen(s);
char s1[251];
char s2[251];
int k;
bool c;
int templen;
for(int i=0; i<len; i++)
{
switch(s[i])
{
case '+': //操作符(前两次循环时s[0]和s[1]肯定都是操作数,因为是后缀表达式嘛)
case '-': //而且都是双目操作符,因此前¡两次switch执行的是default的代码
case '*':
case '/':
if(s[i] == '*' || s[i] == '/')
k = 2; //优先级高
else
k = 1; //优先级低
if(s[i] == '/' || s[i] == '-')
c = true; //可能要添加圆括号
else
c = false; //不需要添加圆括号
//弹出第一个栈顶元素
if(priority[pstack-1]!=0 &&
//堆栈stack中栈顶元素不是操作数(即是操作符)
(priority[pstack-1]<k || (priority[pstack-1]==k && c)))
//栈顶元素是操作符且1)优先级低于当前输入字符
//或者2)当优先级相同,且当前输入字符是'/'或'-'时,需要添加圆括号
{
s2[0] = '(';
s2[1] = '/0';//字符串结束标识º
strcat(s2, stack[pstack-1]);
strcat(s2, ")");
}
else //不需要添加圆括号
{
s2[0] = '/0';
strcat(s2, stack[pstack-1]);
}
//弹出第二个栈顶元素
--pstack;
if(priority[pstack-1]!=0 && priority[pstack-1]<k)//栈顶元素下面一个//元素是操作符,且优先级小于当前输º入字符时º
{
s1[0] = '(';
s1[1] = '/0';
strcat(s1, stack[pstack-1]);
strcat(s1, ")");
}
else //不需加圆括号
{
s1[0] = '/0';
strcat(s1, stack[pstack-1]);
}
strcpy(stack[pstack-1], s1); //s1存入堆栈,即变为当前栈顶元素
templen = strlen(stack[pstack-1]);
stack[pstack-1][templen] = s[i];//将输入字符拼接到s1之后
stack[pstack-1][templen+1] = '/0';
strcat(stack[pstack-1], s2); //将s2拼接到s1+s[i]之后
priority[pstack-1] = k; //设置输入元素s[i]优先级
break;
default: //操作数
stack[pstack][0] = s[i];
stack[pstack][1] = '/0';
priority[pstack] = 0; //操作数的优先级为0
++pstack;
break;
}
}
strcpy(out, stack[0]);
}
int main()
{
int N;
std::cin>>N;
for(int i=0; i<N; i++)
{
pout = 0;//结果字符串out的栈顶指针
memset(out, 0, sizeof(out));//结果字符串堆栈情况
std::cin>>str;
infix2suffix(str); //先将中缀表达式转为后缀表达式º
suffix2infix(out); //再将后缀表达式转为中缀表达式(最少圆括号)
std::cout<<out<<std::endl;
}
system("pause");
return 0;
}