uva 12219 - Common Subexpression Elimination

Description

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. For
example, 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))

CODE

#include<stdio.h>
#include<iostream>
#include<map>
#include<string.h>
#include<string>
using namespace std;

string s;
int k,cnt;
map<int,int> done;

struct tree
{
    string s;
    int ls,rs;
    bool operator < (const tree& rhs) const//要用map一定要重载运算符。
    {
        if(s!=rhs.s)
            return s<rhs.s;
        else if(ls!=rhs.ls)
            return ls<rhs.ls;
        else
            return rs<rhs.rs;
    }
};

map<tree,int> MAP;//保存ID
map<int,tree> NODE;//保存节点信息

int solve()//只扫描一遍,好棒。
{
    string cur;
    while(s[k]>='a'&&s[k]<='z')
        cur.push_back(s[k++]);
    int id=++cnt;//这里是先给你预留一个编号的意思。
    tree& t = NODE[id];//引用,好灵活。
    t.s=cur;
    t.ls=0;
    t.rs=0;
    if(s[k]=='(')
    {
        //写得简单易懂,跳过非字母的字符。
        k++;
        t.ls=solve();k++;
        t.rs=solve();k++;
    }
    if(MAP[t]) //如果MAP[t]!=0,那么cnt必为id+1,cnt--即可取消预留的编号。
    {
        cnt--;
        return MAP[t];
    }
    else 
        return MAP[t]=id;
}
void print(int u)
{
    if(done[u])
        printf("%d",u);
    else
    {
        done[u]=1;
        cout<<NODE[u].s;
        if(NODE[u].ls)
        {
            cout<<"(";
            print(NODE[u].ls);
            cout<<",";
            print(NODE[u].rs);
            cout<<")";
        }
    }
}

int main()
{
    int q;
    cin>>q;
    while(q--)
    {
        MAP.clear();
        done.clear();
        NODE.clear();
        cnt=k=0;
        cin>>s;
        print(solve());//solve返回根编号,print从根标号开始递归输出。
        puts("");
    }
    return 0;
}

代码解释

map内部是利用红黑树排序
红黑树排序只是用’<’所以只需重载’<’
1:a
cur:a
id = cnt = 1;
NODE[1] -> t;//引用
初始化t(NODE[1])
如果这个字符是’(’
{
跳过’(’
对以cur中字符串为根节点的数进行递归
左递归
右递归
}
如果这个树再map中出现了,即这个是公共表达式
cnt–//cnt不用再加了,用那个公共表达式的值即可(例如标记4,两个a都是4)
else
MAP中给这个树标记她的值id

代码部分过程

第一遍:

k:0
cur:a;
k++ == 1;
id = cnt =1;
NODE[1] = t = {a,0,0}
MAP[NODE[1]] = 1;
s[1]:(
k++ == 2
NODE[1].ls->solve()

    第二遍
    k:2
    cur:b;
    k++ == 3
    id = cnt =2;
    NODE[2] = t = {b, 0, 0}
    s[3]:(
    k++ == 4
    NODE[2].ls->solve()

        第三遍
        k:4
        cur:f
        k++ == 5
        id = cnt = 3
        NODE[3] = t = {f,0,0}
        s[5]:(
        k++ == 6
        NODE[3].ls->solve()

            第四遍
            k:6
            cur:a
            k++ == 7
            id = cnt = 4
            NODE[4] = {a,0,0}
            s[7]:,
            return MAP[NODE[4]] == 4;
            {NODE[3].ls = MAP[NODE[4]](4)}

        k++ == 8
        NODE[3].rs->solve()

            k:8
            cur:a
            k++ == 9
            id = cnt = 5
            NODE[5] = {a,0,0}
            s[9]:)
            cnt-- == 4
            return MAP[NODE[5]];
            {NODE[3].rs = MAP[NODE[5]](4)}

        k++ == 10
        MAP[NODE[3]] == 0
        return MAP[NODE[3]] = 3 
        {NODE[2].ls = MAP[NODE[3](3)}

    k++ == 11
    NODE[2].rs->solve()
        k:11
        cur:b
        k++ == 12
        id = cnt = 4
        NODE[4]


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值