1.题目描述:点击打开链接
2.解题思路:第一步是构造表达式树,构造时可以利用一个map来记录出现的子树,并为之编号。例如,用(a,0,0)可以表示一个叶子a,用(b,3,6)表示根的名字是b,子树的编号分别是3,6的树。这样既可方便地得到最简表达式。本题总的时间复杂度为O(N*logN)。
3.代码:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string> #include<sstream> #include<set> #include<vector> #include<stack> #include<map> #include<queue> #include<deque> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<ctime> #include<functional> using namespace std; const int mx = 60000; int T, rnd, cnt; char expr[mx * 5], *p; int done[mx]; struct Node { string s; int ha, left, right; bool operator < (const Node&b)const//由于下面要用到map::count函数,所以必须要重载小于号 { if (ha != b.ha)return ha < b.ha; if (left != b.left)return left < b.left;//哈希相同则比较出现的顺序 return right < b.right; } }node[mx]; map <Node, int>dict;//记录子树的编号 int solve() { int id = cnt++;//从0开始编号 Node&u = node[id]; u.left = u.right = -1; u.s = ""; u.ha = 0; while (isalpha(*p)) { u.ha = u.ha * 27 + *p - 'a' + 1;//可以理解为27进制,a到z的编号为1到26 u.s.push_back(*p);//将该子树的字母放入s p++;//向后扫描,遇到括号停止 } if (*p == '(')//(L,R),递归处理左右子树 { p++;//先跳过'(' u.left = solve(), p++;//返回左子树编号,并跳过',' u.right = solve(),p++;//返回右子树编号,并跳过')' } if (dict.count(u)) { cnt--;//子树出现过,个数减少1 return dict[u];//返回这颗子树的编号 } return dict[u] = id;//如果这棵树是首次出现,给它编号 } void print(int v) { if (done[v] == rnd)printf("%d", v + 1);//已经输出过了,输出序号即可 else { done[v] = rnd;//不需要对done数组初始化,只需要用这一轮特有的rnd标记即可 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() { freopen("test.txt", "r", stdin); scanf("%d", &T); for (rnd = 1; rnd <= T;rnd++) { dict.clear(); cnt = 0; scanf("%s", expr); p = expr;//用指针p扫描expr print(solve()); putchar(10);//打印换行符 } return 0; }
时间: 2024-11-10 13:31:45