一共写了3遍,2遍超时,第三遍看了别人的代码才写出来的。。。
第一遍,直接保存整颗子树,也就是整个子表达式。
为了得到子表达式的值,我频繁调用substr函数。
为了找到分隔左右子表达式的逗号,我直接遍历子表达式。
最后用map去映射。
结果超时。。
后来看了下紫书,说不能保存整颗子树。。
说比较两棵树的时间复杂度是O(n),循环枚举两颗子树,总时间复杂度高达O(n^3)。
我本以为map比较高效的,但后来我仔细算了下发现不会高效很多。
我们都知道map的时间复杂度是O(logn),但是那只是查询的复杂度。如果你查询的是字符串,那就多O(n),每个子树查询一遍,就又多O(n),总共O(n^2logn)。
伤不起。。。
第二遍,建树使扫描整个子表达式找到中间的那个逗号
按书上说的改了下子树的保存方式后,时间复杂度变为O(nlogn),依旧超时。
后来上网看别人的代码,才知道我建树的方法太粗暴了。频繁调运substr+遍历找逗号,都不知道循环多少遍了。
只有扫描一遍的才不会超时。
参考 http://www.bubuko.com/infodetail-646757.html
AC代码
#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;//跟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]) {cnt--;return MAP[t];}//如果MAP[t]!=0,那么cnt必为id+1,cnt--即可取消预留的编号。
else return MAP[t]=id;
}
//原来还可以这样输出。。。不过前两遍的代码我是一遍dfs解决建树与输出的。
//该省的不省。。不该省的乱省。。只能说自己对时间复杂度不够敏感吧。
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;
}
第一遍代码
#include<stdio.h>
#include<iostream>
#include<string>
#include<map>
using namespace std;
struct tree
{
string now;
int l,r;
tree(string a,int b,int c):now(a),l(b),r(c){}
};
map<string,int>MAP;
string s;
int cnt;
void dfs(int l,int r)
{
string a=s.substr(l,r-l+1);
if(MAP[a]) {printf("%d",MAP[a]);return;}
int now=++cnt;
if(s[r]!=')') {cout<<a;MAP[a]=cnt;return;}
while(s[l]!='(') printf("%c",s[l++]);
int k=l;
int p=0;
while(1)
{
if(s[k]=='(') p++;
if(s[k]==')') p--;
if(p==1&&s[k]==',') break;
k++;
}
printf("(");
dfs(l+1,k-1);
printf(",");
dfs(k+1,r-1);
printf(")");
MAP[a]=now;
}
int main()
{
int c;
scanf("%d",&c);
while(c--)
{
MAP.clear();
cnt=0;
cin>>s;
dfs(0,s.size()-1);
puts("");
}
return 0;
}
第二遍代码
#include<stdio.h>
#include<iostream>
#include<string>
#include<sstream>
#include<map>
using namespace std;
struct tree
{
string now;
int l,r;
tree(string a,int b,int c):now(a),l(b),r(c){}
bool operator < (const tree & rhs) const
{
if(now!=rhs.now) return now<rhs.now;
else if(l!=rhs.r) return l<rhs.l;
else return r<rhs.r;
}
};
typedef pair<int,int> pii;
typedef pair<int,string> pis;
map<tree,int>MAP;
string s;
int cnt;
pair<pis,int> dfs(int l,int r)
{
if(s[r]==')')
{
int ll=l;
while(s[ll]!='(') ll++;
int k=ll;
int p=0;
while(1)
{
if(s[k]=='(') p++;
if(s[k]==')') p--;
if(p==1&&s[k]==',') break;
k++;
}
int now=++cnt;
string cur=s.substr(l,ll-l);
stringstream sss;
string ss;
pair<pis,int> ls=dfs(ll+1,k-1);
pair<pis,int> rs=dfs(k+1,r-1);
//cout<<" "<<ls.first.second<<" "<<rs.first.second;
tree t=tree(cur,ls.second,rs.second);
int id=MAP[t];
if(ls.first.first&&rs.first.first&&id)
{
cnt--;
sss<<id;
sss>>ss;
return make_pair(make_pair(1,ss),id);
}
else
{
sss<<cur;
MAP[t]=now;
sss<<"(";
sss<<ls.first.second;
sss<<",";
sss<<rs.first.second;
sss<<")";
sss>>ss;
return make_pair(make_pair(0,ss),now);
}
}
else
{
string ss=s.substr(l,r-l+1);
tree t(ss,0,0);
int id=MAP[t];
stringstream sss;
if(id) sss<<id;
else {MAP[t]=++cnt;sss<<ss;}
sss>>ss;
if(id) return make_pair(make_pair(1,ss),id);
else return make_pair(make_pair(0,ss),cnt);
}
}
int main()
{
int c;
scanf("%d",&c);
while(c--)
{
MAP.clear();
cnt=0;
cin>>s;
cout<<dfs(0,s.size()-1).first.second;
puts("");
}
return 0;
}