【算法学习】【字符串处理】【原题】【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.

posted @ 2010-11-14 15:39  liukee  阅读(595)  评论(0编辑  收藏  举报