题意:
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!
初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:
命令 类型 实现 说明
MKDIR s 操作 在当前目录下创建一个子目录 s,s 是一个字符串 创建成功输出 “OK”;若当前目录下已有该子目录则输出 “ERR”
RM s 操作 在当前目录下删除子目录 s,s 是一个字符串 删除成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
CD s 操作 进入一个子目录 s,s 是一个字符串(执行后,当前目录可能会改变) 进入成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
特殊地,若 s 等于 “…” 则表示返回上级目录,同理,返回成功输出 “OK”,返回失败(当前目录已是根目录没有上级目录)则输出 “ERR”
SZ 询问 输出当前目录的大小 也即输出 1+当前目录的子目录数
LS 询问 输出多行表示当前目录的 “直接子目录” 名 若没有子目录,则输出 “EMPTY”;若子目录数属于 [1,10] 则全部输出;若子目录数大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。
TREE 询问 输出多行表示以当前目录为根的子树的前序遍历结果 若没有后代目录,则输出 “EMPTY”;若后代目录数+1(当前目录)属于 [1,10] 则全部输出;若后代目录数+1(当前目录)大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。若目录结构如上图,当前目录为 “root” 执行结果如下root dira a b c dirb x dirc y(之间有回车)。
UNDO 特殊 撤销操作 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR”。
思路:
基本思路都按照课件的思路来写的,所以就写一写自己思考的东西。
MKDIR:如果不存在节点,要在node[now].mp里添加儿子关系,然后从now向根节点更新子树节点数目sz。接下来要在node[index]里创建新节点,节点所有信息都进行初始化,然后将操作记录在vector v中,以便undo时使用。
RM:只用删除边关系,不用删除节点,以便于undo时恢复边和节点。
UNDO:首先是结构point:string name,int now,int next。name为操作名称,now为当前目录,next为要添加/删除/移到的目录位置。用vector v来记录操作序列。当undo时就取出v尾部的节点,根据节点信息来进行undo操作。
TREE:需要用到遍历子树pushdown操作。而pushdown里要有求子树所有节点alltrack(节点个数<=10),求前五个节点pretrack和后五个节点bcktrack。alltrack只需要前序遍历子树,将节点放在vector pre中即可;pretrack用num来记录还要选几个节点,递归遍历时若num为0则return;bcktrack同样用num来记录,不过遍历是先进入到最右儿子直到到达叶子,然后再取该节点。
总结:
一道非常复杂的模拟题,如果没有上课时所讲的思路,估计几天也a不了,因为会各种超时。以后做题时要先分析复杂度,再根据复杂度来选择数据结构和算法,以免写完代码之后才发现T要进行重构。
同时还发现了自己迭代器的使用不够熟练,begin~end是前闭后开,反向遍历时第一个数是it=end();it–;写的时候写错了,RE调试了好久。
代码:
#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;
struct Directory
{
string name; //名称
map<string,int>mp; //后代的名称和编号
int fa,sz; //父亲节点,子树的规模
vector<string> pre,bck; //存前序遍历的前几个,后几个
bool tag; //是否被更新过
};
Directory node[200000];
int now,index; //当前节点与最新节点
struct point //用于undo储存信息
{
string name; //操作名称
int now; //当前目录
int next; //要添加/删除/移动的目录位置
};
vector<point> v;
void update(int id,int num) //将变化数据向上传导
{
while(id!=-1)
{
node[id].tag=0;
node[id].sz+=num;
id=node[id].fa;
}
}
void MKDIR(string &s) //在当前目录下创建一个子目录s
{
if(node[now].mp.count(s)==1) //存在节点
cout<<"ERR"<<endl;
else
{
node[now].mp.insert(pair<string,int>(s,index));
update(now,1);
//在node[index]创建新节点
node[index].name=s;
node[index].fa=now;
node[index].sz=1;
node[index].tag=0;
node[index].mp.clear();
node[index].pre.clear();
node[index].bck.clear();
//将操作放入undo的vector中
point p;
p.name="MKDIR",p.now=now,p.next=index;
v.push_back(p);
index++;
cout<<"OK"<<endl;
}
}
void RM(string &s) //在当前目录下删除子目录s
{
if(node[now].mp.count(s)==1) //存在节点
{
int indexOfS=node[now].mp[s];
node[now].mp.erase(s);
update(now,(-1)*node[indexOfS].sz);
//将操作放入undo的vector中
point p;
p.name="RM",p.now=now,p.next=indexOfS;
v.push_back(p);
cout<<"OK"<<endl;
}
else cout<<"ERR"<<endl;
}
void CD(string &s) //进入子目录s
{
if(s=="..") //返回上级目录
{
if(node[now].fa==-1) //根目录
cout<<"ERR"<<endl;
else
{
//将操作放到undo的vector中
point p;
p.name="CD",p.now=now,p.next=node[now].fa;
v.push_back(p);
now=node[now].fa;
cout<<"OK"<<endl;
}
}
else //进入子目录s
{
if(node[now].mp.count(s)==1) //存在s
{
//将操作放到undo的vector中
point p;
p.name="CD",p.now=now,p.next=node[now].mp[s];
v.push_back(p);
now=node[now].mp[s];
cout<<"OK"<<endl;
}
else cout<<"ERR"<<endl;
}
}
void SZ() //输出当前目录的大小
{
cout<<node[now].sz<<endl;
}
void LS() //输出当前目录的直接子目录名
{
if(node[now].mp.size()==0) //没有子目录
cout<<"EMPTY"<<endl;
else if(node[now].mp.size()>=1&&node[now].mp.size()<=10) //全部输出
{
for(map<string,int>::iterator it=node[now].mp.begin(); it!=node[now].mp.end(); it++)
cout<<it->first<<endl;
}
else //输出前五个和后五个
{
//前五个
map<string,int>::iterator it=node[now].mp.begin();
for(int i=1; i<=5; i++)
{
cout<<it->first<<endl;
it++;
}
cout<<"..."<<endl;
//后五个
it=node[now].mp.end();
for(int i=1; i<=5; i++)
it--;
for(int i=1; i<=5; i++)
{
cout<<it->first<<endl;
it++;
}
}
}
void alltrack(int id,vector<string> &pre) //遍历id子树,求出全部(小于10个)
{
pre.push_back(node[id].name);
for(map<string,int>::iterator it=node[id].mp.begin(); it!=node[id].mp.end(); it++)
alltrack(it->second,pre);
}
void pretrack(int id,vector<string> &pre,int &num) //遍历id子树,求出前五个
{
pre.push_back(node[id].name);
num--;
if(num==0) return;
for(map<string,int>::iterator it=node[id].mp.begin(); it!=node[id].mp.end(); it++)
{
pretrack(it->second,pre,num);
if(num==0) break;
}
}
void bcktrack(int id,vector<string> &bck,int &num) //遍历id子树,求出后五个
{
map<string,int>::iterator it=node[id].mp.end();
int n=node[id].mp.size();
for(int i=0;i<n;i++)
{
it--;
bcktrack(it->second,bck,num);
if(num==0) return;
}
bck.push_back(node[id].name);
num--;
}
void pushdown(int id) //遍历id子树
{
node[id].pre.clear();
node[id].bck.clear();
if(node[id].sz<=10)
alltrack(id,node[id].pre);
else
{
int num=5;
pretrack(id,node[id].pre,num);
num=5;
bcktrack(id,node[id].bck,num);
}
node[id].tag=1;
}
void TREE() //输出以当前目录为根的子树的前序遍历结果
{
if(!node[now].tag) //需要遍历就去遍历
pushdown(now);
if(node[now].sz==1) //子目录为空
cout<<"EMPTY"<<endl;
else if(node[now].sz>1&&node[now].sz<=10)
{
for(int i=0;i<node[now].pre.size();i++)
cout<<node[now].pre[i]<<endl;
}
else
{
for(int i=0;i<5;i++)
cout<<node[now].pre[i]<<endl;
cout<<"..."<<endl;
for(int i=4;i>=0;i--)
cout<<node[now].bck[i]<<endl;
}
}
void UNDO() //撤销最近一个成功执行的MKDIR或RM或CD
{
if(v.size()==0) //没有操作
cout<<"ERR"<<endl;
else
{
point p=v[v.size()-1];
v.pop_back();
cout<<"OK"<<endl;
if(p.name=="MKDIR") //要删除
{
update(p.now,(-1)*node[p.next].sz);
node[p.now].mp.erase(node[p.next].name);
}
else if(p.name=="RM") //要添加
{
update(p.now,node[p.next].sz);
node[p.now].mp[node[p.next].name]=p.next;
}
else //要返回
now=p.now;
}
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
for(int t1=0; t1<t; t1++)
{
if(t1!=0) //测试数据之间输出空行
cout<<endl;
int q;
cin>>q;
//初始化
//根节点为node[0],其父亲为-1
now=0,index=1;
node[0].name="root",node[0].fa=-1,node[0].sz=1,node[0].tag=0;
node[0].pre.clear(),node[0].bck.clear(),node[0].mp.clear();
v.clear();
for(int q1=0; q1<q; q1++)
{
string op;
cin>>op;
if(op=="MKDIR")
{
string s;
cin>>s;
MKDIR(s);
}
else if(op=="RM")
{
string s;
cin>>s;
RM(s);
}
else if(op=="CD")
{
string s;
cin>>s;
CD(s);
}
else if(op=="SZ")
SZ();
else if(op=="LS")
LS();
else if(op=="TREE")
TREE();
else if(op=="UNDO")
UNDO();
}
}
}