题意:不好描述,总结而言,就是消除公共表达式
题解:使用表达式树,然后再建造表达式树的时候,因为要看后面的子树是否前面出现过,可以使用"集合栈计算机"的思想,利用一个map存储当前字符和两个值存储左儿子编号和右儿子编号,都压入map中进行比较,最后再利用个done数组判断这个子树是否已经输出过,没有输出过输出字符,输出过输出结点编号即可。
有关表达式树的介绍:
二叉树是表达式处理的有效工具,例如表达a+b*(c-d)-e/f,每个非叶节点表示一个运算符,可以利用“找到最后计算"的运算符进行递归处理。
附上代码:
const int maxn=1e3+5;
int lch[maxn],rch[maxn];
char op[maxn];
int nc=0;
int build(char *s,int x,int y)
{
int i,c1=-1,c2=-1,p=0;
int u;
if(y-x==1){//仅一个字符,建立单独结点
u=++nc;
lch[u]=rch[u]=0;
op[u]=s[x];
return u;
}
for(i=x;i<y;i++){
switch(s[i]){
case '(':p++;break;
case ')':p--;break;
case '+':case '-':if(!p)c1=i;break;
case '*':case '/':if(!p)c2=i;break;
}
}
if(c1<0){//找不到括号外的加减号,就用乘除号
c1=c2;
}
if(c1<0){//整个表达式被一对括号括起来
return build_tree(s,x+1,y-1);
}
u=++nc;
lch[u]=build_tree(s,x,c1);
rch[u]=build_tree(s,c1+1,y);
op[u]=s[c1];
return u;
}
如何寻找最后一个运算符,先找括号外面的,如果没有去掉括号继续找。
附上本题代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e4+50;
int T,kase,cnt;
char expr[maxn*5],*p;
int done[maxn];
struct Node{
string s;
int hash,left,right;
bool operator < (const Node&rhs)const{
if(hash!=rhs.hash){
return hash<rhs.hash;
}
if(left!=rhs.left){
return left<rhs.left;
}
return right<rhs.right;
}
}node[maxn];
map<Node,int>dict;
int parse() {
int id = cnt++;
Node& u = node[id];
u.left = u.right = -1;
u.s = "";
u.hash = 0;
while(isalpha(*p)) {
u.hash = u.hash * 27 + *p - 'a' + 1;
u.s.push_back(*p);
p++;
}
if (*p == '(') { // (L,R)
p++; u.left = parse(); p++; u.right = parse(); p++;
}
if (dict.count(u) != 0) {
cnt--;
return dict[u];
}
return dict[u] = id;
}
void print(int v) {
if(done[v] == kase)
printf("%d", v + 1);
else {
done[v] = kase; // 常见小技巧,可以避免memset(done, 0, sizeof(done))
printf("%s", node[v].s.c_str());
if(node[v].left != -1) {
putchar('(');
print(node[v].left);
putchar(',');
print(node[v].right);
putchar(')');
}
}
}
int main()
{
scanf("%d",&T);
for(kase=1;kase<=T;kase++){
dict.clear();
cnt=0;
scanf("%s",expr);
p=expr;
print(parse());
putchar('\n');
}
return 0;
}