UVA 12219 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. 
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 he 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))

Source 

总结:

啊!!这道题从一开就知道写起来会很麻烦,但是之前一直会出现用map<Node ,int>出错的问题,这道题刚好用到了map来检查相同元素是否出现过,问题应该是出现在了结构题中,如果我们没有对结构题的<重载的话stl是不能够判断两个结构题是否相同的,并且我们还可以用hash来解决匹配问题,debug时间非常长,最近经常用很长的时间来dug,问题往往都是一些细微的地方,变量指向的位置,变量的范围等,所以在每一步进行运算的时候一定要小心啊!!
从思路上来讲,和之前搞的一道表达式树的写法有所不同,数据结构构造的十分精巧,一开始也是想要通过index的方式来构造二叉树,但是经过思考这样的数据结构和map来比较node不兼容,但是用编号系统就很容易解决了这个问题!
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <map>
#include <ctype.h>
using namespace std;
const int maxn=60000+10;
int t,cnt,done[maxn];
char str[maxn*5],*p;
struct Node
{
    int hash;
    string s;
    int left;
    int right;
    bool operator <(const Node &b) const
    {
        if(hash!=b.hash) return hash<b.hash;
        if(left!=b.left) return left<b.left;
        return right<b.right;
    }
}Nodes[maxn];
map <Node,int> dict;
int build()
{
    int id=cnt++;
    Node &u=Nodes[id];
    u.left=-1;
    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=='(')
    {
        p++;
        u.left=build(); p++;
        u.right=build(); p++;
    }
    if(dict.count(u)==1)
    {
        cnt--;
        return dict[u];
    }
    else
    { return dict[u]=id;}
}
void print(int d)
{
    if(done[d]==t) printf("%d",d+1);
    else
    {
        done[d]=t;
        printf("%s",Nodes[d].s.c_str());
        if(Nodes[d].left!=-1)
        {
            printf("(");
            print(Nodes[d].left);
            printf(",");
            print(Nodes[d].right);
            printf(")");
        }
    }
}


int main()
{
   // freopen("/Users/zhangjiatao/Documents/暑期训练/input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    for(t=1;t<=T;t++)
    {
        dict.clear();
        //memset(done,0,sizeof(done));
        scanf("%s",str);
        p=str;
        cnt=0;
        print(build());
        printf("\n");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值