1. 程式人生 > >Common Subexpression Elimination UVA - 12219(表示式樹)

Common Subexpression Elimination UVA - 12219(表示式樹)

傳送門

題意:不好描述,總結而言,就是消除公共表示式

題解:使用表示式樹,然後再建造表示式樹的時候,因為要看後面的子樹是否前面出現過,可以使用"集合棧計算機"的思想,利用一個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;
}