Common Subexpression Elimination UVA - 12219(表示式樹)
阿新 • • 發佈:2018-12-26
題意:不好描述,總結而言,就是消除公共表示式
題解:使用表示式樹,然後再建造表示式樹的時候,因為要看後面的子樹是否前面出現過,可以使用"集合棧計算機"的思想,利用一個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; }