题解(基于紫书)
写在前面:基于紫书给出的思路,自己动手写的代码,运行时间700ms
大致思路
O(n)时间建表达式树;一次dfs:map查重、标号;一次dfs打印答案
讲解
因为按照书上的建树代码,会超时,dfs部分较简单,所以主讲建树。
O(n)时间建表达式树
借鉴书上经验,用一个变量ord记录“经过”的括号,遇到左括号"(" 则加一,遇到右括号")" 则减一。由于除了根结点,所有结点都有父亲,数组record记录ord位置上的结点序号,则对于当前结点,record[ord-1]即为父结点——因为子结点相对父结点被一对括号包裹,即他们相对父结点 “多出”一个左括号。初始化记录左右子的数组为0,当存在ord时,(除了根节点)ord-1必是ord的父结点(没有子结点的结点ord一定不会在后面产生它的子代ord+1,即使后面出现ord+1这个序号,此时的ord已经不是上文提到的那个ord了),至于ord-1是左子还是右子,由于有子代必有两个子代(依题意得),则那个是空的就填那个。
![](https://i-blog.csdnimg.cn/blog_migrate/c4d5c8b7033f0ee82e0858c524fa6cdd.jpeg)
小细节
getchar实现一个一个读取一行输入,因为结点名字长度在1~4字符,不定长,需要根据后面出现"(", ")", "," 来判断一个结点读取完毕,故用一个变量writing标记“第一次读取完毕,”且需要特判只有一个结点的情况(即只有根结点)
关于map
我的key是一个struct,即书上的“用根的名字和左右子结点编号表示”,需要注意为struct重载小于号,否则判重(count)时会出错
关于dfs
由于map查重要看整颗子树(即先看子代,子代看完了再看父代),dfs标号又是从父到子,矛盾,一次dfs只能标号,需要再用一次dfs打印
第一次dfs先顺着标下去(cnt),子代查完重再查父代,即逆着修改。ans保存了最终的正确序号:不重复的保存的是序号,重复的保存的是它的模板的序号,那么第二次dfs打印时,再维护一个序号标记,对比ans,对得上的打印结点的名字,对不上说明前一次dfs标记为重复,打印模板的序号即可。
AC代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <map>
using namespace std;
const int maxn=50000+3;
int lch[maxn],rch[maxn];// children tree
int nc;// order of node
int ans[maxn];
int cnt;// final order counter
struct state{
char nm[5];
int chi[2];
state(const char* str,const int* p){
memcpy(nm,str, sizeof(char)*5);
memcpy(chi,p, sizeof(int)*2);
}
bool operator < (const state& rhs) const {
int cmp= strcmp(nm,rhs.nm);
return cmp<0|| (cmp==0 && chi[0]<rhs.chi[0]) ||
(cmp==0 && chi[0]==rhs.chi[0] && chi[1]<rhs.chi[1]);
}
};
map<state,int>node;
char op[maxn][5];
int record[maxn/2+3];// order
void build(){
char c;
char name[5];
int len=0;
int ord=1;// bracket
bool writing;
record[0]=0;
bool single=true;// special judge: single input
while ((c=getchar())!='\n'){
if(c!='(' && c!=')' && c!=',') {
name[len++]=c;
writing=true;
continue;
}
if(writing) {
writing=false;
single=false;
name[len]='\0';
len=0;
int u=++nc;
memcpy(op[u],name, sizeof(char)*5);
int fa=record[ord-1];
record[ord]=u;
if(lch[fa]==0) lch[fa]=u;
else rch[fa]=u;
}
switch (c) {
case '(':++ord;break;
case ')':--ord;break;
default:continue;
}
}
if(single) {
name[len]='\0';
memcpy(op[1],name, sizeof(char)*5);
}
}
int dfs(int ord){
if(ord==0) return 0;
ans[ord]=++cnt;
char* root=op[ord];
int chi[2];
chi[0]= dfs(lch[ord]);
chi[1]= dfs(rch[ord]);
state st(root,chi);
if(node.count(st)) {
cnt=ans[ord]-1;// no count
ans[ord]=node[st];
}else node[st]=ans[ord];
return ans[ord];
}
void _print(int ord,int& f_ord) {
if(f_ord==ans[ord]) {
printf("%s",op[ord]);
++f_ord;// next node
if(lch[ord]==0) return;
else {
printf("(");
_print(lch[ord],f_ord);
printf(",");
_print(rch[ord],f_ord);
printf(")");
}
}
else {
printf("%d",ans[ord]);
return;
}
}
int main(){
int T;
scanf("%d",&T);
getchar();// empty character
while (T--){
// initialize
node.clear();
nc=0;// order: root-lch-rch; and start from no.1
memset(ans,0, sizeof(ans));
memset(lch,0, sizeof(lch));
memset(rch,0, sizeof(rch));
build();
cnt=0;
dfs(1);
int f_ord=1;
_print(1,f_ord);
printf("\n");
}
return 0;
}