【算法学习】【字符串处理】【原题】【noip 2005 T4】等价表达式
我们今天要介绍的算法,就是表达式求值。
表达式就是大家熟知的中缀表达式,有两种方法,表达式树和栈。在这里我们只介绍第二种——栈
先将中缀表达式转为后缀表达式
code
program liukee; var zhong,hou:string;//存储两个表达式 stack:array[1..5000] of char; ch,temp:char; top,i:longint; procedure init; begin read(ch); while(ch<>'=')and(ord(ch)>=40)and(ord(ch)<=57)and(ord(ch)<>44)and(ord(ch)<>46)or(ord(ch)=94)do begin zhong:=zhong+ch; read(ch); end; end; begin init; i:=1; top:=0; hou:=''; while i<=length(zhong) do begin case zhong[i] of '0'..'9'://如果中缀表达式中是数字的话,直接加入到后缀表达式中 begin while zhong[i] in ['0'..'9'] do begin hou:=hou+zhong[i]; inc(i); end; hou:=hou+';';//防止多位数出现 dec(i); end; '('://左括号直接入栈 begin inc(top); stack[top]:='('; end; ')'://如果是右括号,则弹出与它相距最近的左括号之间的运算符,以及左括号 begin while(top>0)and(stack[top]<>'(')do begin hou:=hou+stack[top]; dec(top); end; dec(top); end; '+','-'://如果是加号减号,弹出比起运算优先的运算符 begin while(top>0)and((stack[top]='+')or(stack[top]='-')or(stack[top]='*')or(stack[top]='/')or(stack[top]='^')) do begin hou:=hou+stack[top]; dec(top); end; inc(top); stack[top]:=zhong[i];//当前算符入栈 end; '*','/'://如果是乘号和除号,则弹出优先算符 begin while(top>0)and((stack[top]='*')or(stack[top]='/')or(stack[top]='^'))do begin hou:=hou+stack[top]; dec(top); end; inc(top);//当前算符入栈 stack[top]:=zhong[i]; end; '^'://如果读到次方,直接入栈,因为它的运算比谁都优先 begin inc(top); stack[top]:='^'; end; end;//case inc(i); end; while top>0 do//将栈中剩余元素加入表达式 begin hou:=hou+stack[top]; dec(top); end; hou:=hou+'=';//标记构造完毕 writeln(hou); end.
再计算后缀表达式的值
code
program liukee;//后缀表达式求值。 var s:array[1..100] of longint; hou:string; top,i,j,k,temp:longint; function cifang(a,b:longint):longint; var ans:longint; begin if a=1 then exit(1); ans:=1; while b>0 do begin if b and 1=1 then ans:=ans*a; a:=a*a; b:=b>>1; end; exit(ans); end; begin readln(hou); i:=1;top:=0; while hou[i]<>'=' do begin case hou[i] of '0'..'9': begin k:=0;//为了处理多位数 while hou[i]<>';' do//找到当前数字 begin k:=10*k+ord(hou[i])-48; inc(i); end; dec(i); inc(top);//将当前数字入栈 s[top]:=k; end; //如果读到运算符。则取出当前栈顶的两个元素,计算之后再压入栈中 '+': begin temp:=(s[top-1]+s[top]); dec(top); s[top]:=temp; end; '-': begin temp:=(s[top-1]-s[top]); dec(top); s[top]:=temp; end; '*': begin temp:=(s[top-1]*s[top]); dec(top); s[top]:=temp; end; '/': begin temp:=(s[top-1] div s[top]); dec(top); s[top]:=temp; end; '^': begin temp:=cifang(s[top-1],s[top]);//快速幂 dec(top); s[top]:=temp; end; end;//case inc(i); end; writeln(s[top]); end.
我们看一道例题
问题
描述 Description
明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。
这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?
这个选择题中的每个表达式都满足下面的性质:
1. 表达式只可能包含一个变量‘a’。
2. 表达式中出现的数都是正整数,而且都小于10000。
3. 表达式中可以包括四种运算‘+’(加),‘-’(减),‘*’(乘),‘^’(乘幂),以及小括号‘(’,‘)’。小括号的优先级最高,其次是‘^’,然后是‘*’,最后是‘+’和‘-’。‘+’和‘-’的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符‘+’,‘-’,‘*’,‘^’以及小括号‘(’,‘)’都是英文字符)
4. 幂指数只可能是1到10之间的正整数(包括1和10)。
5. 表达式内部,头部或者尾部都可能有一些多余的空格。
下面是一些合理的表达式的例子:
((a^1) ^ 2)^3,a*a+a-a,((a+a)),9999+(a-a)*a,1 + (a -1)^3,1^10^9……
输入格式 Input Format
输入文件 的第一行给出的是题干中的表达式。第二行是一个整数 n ( 2 <= n <= 26 ),表示选项的个数。后面 n 行,每行包括一个选项中的表达式。这 n 个选项的标号分别是 A , B , C , D ……
输入中的表达式的长度都不超过 50 个字符,而且保证选项中总有表达式和题干中的表达式是等价的。
输出格式 Output Format
输出文件 包括一行,这一行包括一系列选项的标号,表示哪些选项是和题干中的表达式等价的。选项的标号按照字母顺序排列,而且之间没有空格。
分析
显然,这道题要用到表达式求值的知识。我们只需带入两组数值(保险起见)。再看两式结果是否相等即可。注意,中间结果可能很大,所以我们在每次计算时要mod一个大质数。
反思
noip也会考察很基本的代码,但是如果是这样的基础,却不会打的话,就是悲剧了。
code
program liukee; var s1:array[0..100] of char;//两个栈,存储算符和数字 s2:array[0..100] of longint; t1,t2,i,temp1,temp2,ans,goal1,goal2,n:longint; s,zhong,hou:string; num:char; function cf(a,b:longint):longint; var ans:longint; begin if (a=1)or(b=1) then exit(a); ans:=1; while b>0 do begin if b and 1=1 then ans:=ans*a; a:=a*a mod 35111; b:=b>>1; end; exit(ans); end; function work(s:string):longint; var i,j,k,temp:longint; ///hou,zhong:string; begin zhong:=''; hou:=''; t1:=0; t2:=0; fillchar(s1,sizeof(s1),' '); fillchar(s2,sizeof(s2),0); for i:=1 to length(s) do if s[i]<>' ' then zhong:=zhong+s[i]; for i:=1 to length(zhong) do if zhong[i]='a' then zhong[i]:=num; //中缀表达式转后缀表达式 i:=1; while i<=length(zhong)do begin case zhong[i] of '0'..'9': begin while (zhong[i] in ['0'..'9'])and(i<=length(zhong)) do begin hou:=hou+zhong[i]; inc(i); end; hou:=hou+';';//题目中说有多位数,标记当前数 dec(i); end; '(': begin inc(t1); s1[t1]:='('; end; ')': begin while(t1>0)and(s1[t1]<>'(')do begin hou:=hou+s1[t1]; dec(t1); end; dec(t1); end; '+','-': begin while(t1>0)and((s1[t1]='+')or(s1[t1]='-')or(s1[t1]='*')or(s1[t1]='^'))do begin hou:=hou+s1[t1]; dec(t1); end; inc(t1); s1[t1]:=zhong[i]; end; '*': begin while(t1>0)and((s1[t1]='*')or(s1[t1]='^'))do begin hou:=hou+s1[t1]; dec(t1); end; inc(t1); s1[t1]:=zhong[i]; end; '^': begin while(t1>0)and(s1[t1]='^') do begin hou:=hou+s1[t1]; dec(t1); end; inc(t1); s1[t1]:=zhong[i]; end; end;//case inc(i); end;//while; while(t1>0)do begin hou:=hou+s1[t1]; dec(t1); end; //-------------------------后缀表达式求值------------------------------------------------- i:=1; while i<=(length(hou)) do begin case hou[i] of '0'..'9': begin k:=0; while hou[i]<>';' do begin k:=(k*10)+ord(hou[i])-48; inc(i) end; inc(t2); s2[t2]:=k; dec(i); end; '*': begin temp:=(s2[t2-1]*s2[t2]) mod 35111; dec(t2); s2[t2]:=temp; end; '+': begin temp:=(s2[t2-1]+s2[t2]) mod 35111; dec(t2); s2[t2]:=temp; end; '-': begin temp:=(s2[t2-1]-s2[t2]) mod 35111; dec(t2); s2[t2]:=temp; end; '^': begin temp:=cf(s2[t2-1],s2[t2]) mod 35111; dec(t2); s2[t2]:=temp; end; end;//case; inc(i); end;//while; exit(s2[t2]); end; begin readln(s); num:='2'; goal1:=work(s); num:='3'; goal2:=work(s); readln(n); for i:=1 to n do begin readln(s); num:='2'; temp1:=work(s); num:='3'; temp2:=work(s); if (temp1=goal1) and (temp2=goal2) then write(chr(i+64)); end; end.