公共表达式消除(common_subexpression_elimination)

https://blog.csdn.net/u014800748/article/details/43975097

https://www.luogu.com.cn/blog/ljcljc/solution-uva12219

【分析】

题目的意思是要求化简子树,使每一个子树都唯一存在,然而我们可以用一个结构体Node(x,l,r)Node(x,l,r)代表整个子树,xx代表当前子树根的字符串,ll代表当前子树的左子树,rr代表当前子树的右子树。每次我们可以用mapmap去实现O(logn)O(logn)的查询,建树的时候直接从左到右扫描字符串,如果当前字符是'((',则递归,继续建树;否则则表示当前的子树已经构建完成,给每一个子树一个旧编号,用一个固定数组存下来,遍历的时候用题目要求的新编号取代旧编号递归输出即可。

【丑陋的AC代码】

#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
const int MAXN = 60010;
int T, Kase, cnt, done[MAXN];
char c[MAXN*5], *p;
struct Node
{
    string s;
    int x, l, r;
    bool operator < (const Node&b)const//由于下面要用到map::count函数,所以必须要重载小于号
    {   
        if (x != b.x)
            return x < b.x;
        if (l != b.l)
            return l < b.l;//哈希相同则比较出现的顺序
        return r < b.r;
    }
}node[MAXN];
map<Node, int> ID;//记录子树的编号
int solve()
{
    int id = cnt++;//从0开始编号
    Node&u = node[id];
    u.l = u.r = -1;
    u.s = "";   
    u.x = 0;
    while (isalpha(*p))
    {
        u.x = u.x * 27 + *p - 'a' + 1;//可以理解为27进制,a到z的编号为1到26
        u.s.push_back(*p);//将该子树的字母放入s
        p++;//向后扫描,遇到括号停止
    }
    if (*p == '(')//(L,R),递归处理左右子树
    {
        p++;//先跳过'('
        u.l = solve(),p++;//返回左子树编号,并跳过','
        u.r = solve(),p++;//返回右子树编号,并跳过')'
    }
    if (ID.count(u))
    {
        cnt--;//子树出现过,个数减少1
        return ID[u];//返回这颗子树的编号
    }
    return ID[u] = id;//如果这棵树是首次出现,给它编号
}
void print(int v)
{
    if (done[v] == Kase)
        printf("%d", v + 1);//已经输出过了,输出序号即可
    else
    {
        done[v] = Kase;//不需要对done数组初始化,只需要用这一轮特有的Kase标记即可
        printf("%s", node[v].s.c_str());//输出树根的字母
        if (node[v].l != -1)//含有左右子树
        {
            putchar('(');
            print(node[v].l);//递归输出左右子树
            putchar(',');
            print(node[v].r);
            putchar(')');
        }
    }
}

int main()
{
    scanf("%d", &T);
    for(Kase = 1; Kase <= T; Kase ++)
    {
        ID.clear();
        cnt = 0;
        scanf("%s", c);
        p = c;//用指针p扫描c
        print(solve());
        printf("\n");
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值