uva12219 Common Subexpression Elimination (表达式树,map)

Common Subexpression Elimination

Let the set Σ consist of all words composed of 1-4 lower case letters, such as the words “a”, “b”, “f”,
“aa”, “fun” and “kvqf”. Consider expressions according to the grammar with the two rules

E → f
E → f(E, E)

for every symbol f ∈ Σ. Any expression can easily be represented as a tree according to its syntax. Forexample, the expression “a(b(f(a,a),b(f(a,a),f)),f(b(f(a,a),b(f(a,a),f)),f))” is represented
by the tree on the left in the following figure:
在这里插入图片描述
Last night you dreamt of a great invention which considerably reduces the size of the representation:
use a graph instead of a tree, to share common subexpressions. For example, the expression above can be represented by the graph on the right in the figure. While the tree contains 21 nodes, the graph just contains 7 nodes.
Since the tree on the left in the figure is also a graph, the representation using graphs is not
necessarily unique. Given an expression, find a graph representing the expression with as few nodes as possible!

Input

The first line of the input contains the number c (1 ≤ c ≤ 200), the number of expressions. Each of
the following c lines contains an expression according to the given syntax, without any whitespace. Its tree representation contains at most 50 000 nodes.

Output

For each expression, print a single line containing a graph representation with as few nodes as possible. The graph representation is written down as a string by replacing the appropriate subexpressions with numbers. Each number points to the root node of the subexpression which should be inserted at that position. Nodes are numbered sequentially, starting with 1; this numbering includes just the nodes of the graph (not those which have been replaced by numbers). Numbers must point to nodes written down before (no forward pointers). For our example, we obtain ‘a(b(f(a,4),b(3,f)),f(2,6))’.

Sample Input

3
this(is(a,tiny),tree)
a(b(f(a,a),b(f(a,a),f)),f(b(f(a,a),b(f(a,a),f)),f))
z(zz(zzzz(zz,z),zzzz(zz,z)),zzzz(zz(zzzz(zz,z),zzzz(zz,z)),z))

Sample Output

this(is(a,tiny),tree)
a(b(f(a,4),b(3,f)),f(2,6))
z(zz(zzzz(zz,z),3),zzzz(2,5))

题意:

可以用表达式树来表示一个表达式,如表达式a(b(f(a,a),b(f(a,a),f)),f(b(f(a,a),b(f(a,a),f)),f))可以用题目中的图一表示。
用消除公共表达式的方法可以减少树上的节点,得到一个新图,如题目中的图二,其表示方法变为a(b(f(a,4),b(3,f)),f(2,6))。其中每个节点按照出现顺序编号为1,2,3…,编号k表示第k次出现的节点

思路:

首先肯定要构造表达式树,然后就是判断是否有重复节点。
直接两两比较两棵树肯定超时,这题不仅需要快速的比较两棵树是否相同,而且还需要快速查询某棵树是否出现过。

利用map标记树的节点就可以做到快速查询是否某个节点是否出现过,用map把每棵子树映射成一个编号,这样一来每次判断一棵子树是否出现过就非常块了。
具体操作方法见代码。

ps:
这题还学到一个标记数组vis的技巧,平常每组数据之前都需要用memset把vis清零,
但是如果有T组数据的化,假设当前为第cas组,vis[x]为cas相当于平常的vis[x]为1,否则为0
这样就可以不清空vis数组了,妙啊
代码里面用了这种方法

code:

#include <bits/stdc++.h>
using namespace std;
//#define int long long
const int maxm=5e4+5;
struct Node{
    string s;
    int hash,lc,rc;//hash存hash值,lc和rc存左右节点编号
    bool operator<(const Node a)const{//map需要有序,随便定一个排序规则就行了
        if(hash!=a.hash)return hash<a.hash;
        if(lc!=a.lc)return lc<a.lc;
        return rc<a.rc;
    }
}e[maxm];
//
map<Node,int>mark;
int cnt;
//
int vis[maxm];
int cas;//当前案例编号(1-T)
//
string ss;//存题目输入的字符串
int cur;
//
int solve(){
    int id=++cnt;
    Node &a=e[id];
    a={"",0,0,0};//初始化
    while(isalpha(ss[cur])){//用27进制数来表示字符串,且取消0,1~27对应a~b
        a.hash=a.hash*27+(ss[cur]-'a'+1);
        a.s+=ss[cur];
        cur++;
    }
    if(ss[cur]=='('){
        cur++;//跳过'('
        a.lc=solve();
        cur++;//跳过')'
        a.rc=solve();
        cur++;//跳过','
    }
    if(mark.count(a)){//如果已经出现过了就不重复标记
        id--;
        cnt--;
        return mark[a];
    }
    return mark[a]=id;
}
void print(int x){
    if(vis[x]==cas){//如果出现过
        printf("%d",x);//这里的vis利用cas标记,省去了vis的清零操作
    }else{
        vis[x]=cas;//省去了vis的清零操作
        cout<<e[x].s;
        if(e[x].lc){//如果还有子树则继续递归输出
            putchar('(');
            print(e[x].lc);
            putchar(',');
            print(e[x].rc);
            putchar(')');
        }
    }
}
signed main(){
    int T;
    cin>>T;
    while(++cas<=T){
        mark.clear();
        cin>>ss;
        cur=cnt=0;
        print(solve());
        cout<<endl;
    }
    return 0;
}
发布了364 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/103411440