题意
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
输入
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
输出
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
样例输入
input:
1
22
MKDIR dira
CD dirb
CD dira
MKDIR a
MKDIR b
MKDIR c
CD …
MKDIR dirb
CD dirb
MKDIR x
CD …
MKDIR dirc
CD dirc
MKDIR y
CD …
SZ
LS
TREE
RM dira
TREE
UNDO
TREE
output:
OK
ERR
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
9
dira
dirb
dirc
root
dira
a
b
c
dirb
x
dirc
y
OK
root
dirb
x
dirc
y
OK
root
dira
a
b
c
dirb
x
dirc
y
思路
- 首先我们应该去思考我们需要进行怎样的操作,这些操作之间有无关联,可不可以进行封装,使用面向对象进行解决这个问题,题目中明显的看出这是一个树的结构,操作对应着树的动态创建和删除等操作;
- 我们发现几乎全部的操作都是单独的对树进行操作,我们就可以创建一个树的类,然后将操作封装,其次就是应该定义树的结点结构,以及我们发现undo操作等,需要对上一个命令进行恢复操作,那么我们还需要保存命令,那么,命令依旧不能是一个单独的变量,我们同样将命令进行封装为一个结构体。
- 对于MKDIR操作,创建子目录,本质上就是创建子节点,我们对于每一个结点与其子节点使用map数组进行一个映射,方便直接通过迭代器进行查询,以及删除;所以对于创建结点就只是map[name]=child,然后要进行其父节点子树规模的更新。
- 对于RM操作,就是通过迭代器进行一个结点的删除 ,mp.erase(it);
- 对于CD操作,首先要进行判断是不是返回当前目录,是的话,返回其FA结点,如果不是,返回其child结点;
- 对于SZ操作,就是输入其子树的规模;
- 对于LS操作,需要进行分层操作,根据子目录的多少,如果是0,那么就是“EMPTY",如果小于10 ,则直接通过迭代器进行mp的输出,如果大于10,那么就先使用迭代器输出前五个,然后将迭代器直接以到末位,然后末尾向前先偏移5个,然后向后输入后五个;
- 对于tree操作,还是进行分层操作,对于子目录小于十个的,直接一个一个输出即可,对于大于10个,我们需要考虑进行前五个的操作和后五个目录的操作,分别使用两个递归函数进行解决即可,此题由于数据范围比较大,那我们如果对于tree操作每次进行便利的话,肯定会超时,那么此时我们记录目录即可,如果没有更新直接读取即可,此处的记忆性体现在RM操作中的记录。
- 对于UNDO操作,我们之前就在命令结构体中,记录了当前操作结点,其次我们需要一个vector数组记录每一个命令,以明确命令的先后关系。还有一点就是UNDO操作只是会针对MKDIR\RM\CD操作,那么我们就需要记住MIKDIR创建的结点,RM删除的结点,然后进行相应的处理即可。
总结
此题对于我来说,就是一个复杂问题的对象化,充分使用面向对象进行解决问题,将方法全部进行封装,有利于维护,具有整体性;
然后就是解决此题的一个层次化问题,要考虑全面,到底这个结构能不能进行使用,使用了对于后面的操作有没有不便利之处,要进行全局考虑。
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
struct Directory
{
string name; //当前目录名称
map<string, Directory*> mp; //所有后代建立名称的映射
Directory* fa; //父目录
int subtreesize; //子树规模
bool updated;
vector<string> tenChild; //保存节点的十个后代
Directory(string name, Directory* fa)
{
this->name = name;
this->fa = fa;
this->subtreesize = 1;
}
public:
Directory* getChild(string name);//获得字目录
Directory* mkdir(string name); //创建新目录
Directory* rm(string name);
Directory* cd(string name);
bool addChild(Directory* ch);
void maintain(int x);//更新之前的子树大小
void sz();
void ls();
void tree();
private:
void treeAll(vector<string>* bar);//后代加入桶
void treefirst(int num, vector<string>* bar);//前序
void treelast(int num, vector<string>* bar);//后序
};
struct Command {
const string CMDNAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
int type; //命令类型
string arg; //命令参数
Command(string s)
{
for (int i = 0; i < 7; i++)
if (CMDNAMES[i] == s)
{
type = i;
string temps;
if (i < 3) {
cin >> temps;
arg = temps;
}
return;
}
}
Directory* temp;//记录刚刚操作的目录,以防UNDO
};
Directory* Directory::getChild(string name)
{
map<string, Directory*>::iterator it = mp.find(name);
if(it==mp.end())
return nullptr;
return it->second;
}
Directory* Directory::mkdir(string name)
{
if(mp.find(name)!=mp.end())//若当前目录下已有该目录
return nullptr;
Directory* child = new Directory(name, this);
mp[name] = child;
maintain(1);//更新之前的子树
return child;
}
Directory* Directory::rm(string name)
{
map<string, Directory*>::iterator it = mp.find(name);
if(it==mp.end())//如果不存在该目录
return nullptr;
maintain(-1 * it->second->subtreesize);//删除子目录以及其子树
mp.erase(it);//删除子目录
return it->second;//返回子目录的下一个兄弟目录
}
Directory* Directory::cd(string name)
{
if ( name=="..")//返回上级目录
return this->fa;
return getChild(name);
}
bool Directory::addChild(Directory* ch)
{
if(mp.find(ch->name)!=mp.end())//如果存在该字目录
return false;
mp[ch->name] = ch;
maintain(+ch->subtreesize);
return true;
}
void Directory::maintain(int x)//维护向上的sz
{
updated = true;
subtreesize = subtreesize + x;
if (fa != nullptr)
fa->maintain(x);
}
void Directory::sz()//返沪子目录的规模
{
cout<<subtreesize<<endl;
}
void Directory::ls()
{
int s = mp.size();
if (s == 0)
cout<<"EMPTY"<<endl;
else if (s <= 10)
{
map<string, Directory*>::iterator it = mp.begin();
for (; it != mp.end(); it++)
cout<<it->first<<endl;
}
else
{
map<string, Directory*>::iterator it = mp.begin();
for (int i = 0; i < 5; i++, it++)
cout<<it->first<<endl;
cout<<"..."<<endl;
it = mp.end();
for (int i = 0; i < 5; i++) it--;
for(int i=0;i<5;i++,it++)
cout<<it->first<<endl;
}
}
void Directory::tree()
{
if (subtreesize == 1)
cout<<"EMPTY"<<endl;
else if (subtreesize <= 10)
{
if (this->updated)
{
tenChild.clear();
treeAll(&tenChild);
this->updated = false;
}
for (int i = 0; i < subtreesize; i++)
cout<<tenChild.at(i)<<endl;
}
else
{
if (this->updated)
{
tenChild.clear();
treefirst(5, &tenChild);
treelast(5, &tenChild);
this->updated = false;
}
for (int i = 0; i < 5; i++)
cout<<tenChild.at(i)<<endl;
cout<<"..."<<endl;
for(int i=9;i>=5;i--)
cout<<tenChild.at(i)<<endl;
}
}
void Directory::treeAll(vector<string>* bar)
{//更新全桶
bar->push_back(name);
map<string, Directory*>::iterator it = mp.begin();
for (; it != mp.end(); it++)
it->second->treeAll(bar);
}
void Directory::treefirst(int num, vector<string>*bar)
{
bar->push_back(name);
if (--num == 0) return;
int n = mp.size();
map<string, Directory*>::iterator it = mp.begin();
while (n--)
{
int sz1 = it->second->subtreesize;
if (sz1 >= num)
{
it->second->treefirst(num, bar);
return;
}
else
{
it->second->treefirst(sz1, bar);
num = num - sz1;
}
it++;
}
}
void Directory::treelast(int num, vector<string>* bar)
{
int n = mp.size();
map<string, Directory*>::iterator it = mp.end();
while (n--)
{
it--;
int sz1 = it->second->subtreesize;
if (sz1 >= num)
{
it->second->treelast(num, bar);
return;
}
else
{
it->second->treelast(sz1, bar);
num = num - sz1;
}
}
bar->push_back(name);
}
void solve()
{
int n;
cin>>n;//操作命令条数
Directory* now = new Directory("root", NULL);//当前目录
vector<Command*> cmdList;//为UNDO做准备
for(int i=0;i<n;i++)
{
string temps;//命令
cin >> temps;
Command* cmd = new Command(temps);
switch (cmd->type)
{
case 0:
cmd->temp= now->mkdir(cmd->arg);
if (cmd->temp == NULL)
cout<<"ERR"<<endl;
else
{
cout<<"OK"<<endl;
cmdList.push_back(cmd);
}
break;//MKDIR
case 1:
cmd->temp = now->rm(cmd->arg);
if (cmd->temp == NULL)
cout<<"ERR"<<endl;
else
{
cout<<"OK"<<endl;
cmdList.push_back(cmd);
}
break;//RM
case 2: {
Directory* ch = now->cd(cmd->arg);
if (ch == NULL)
cout<<"ERR"<<endl;
else
{
cout<<"OK"<<endl;
cmd->temp = now;
now = ch;
cmdList.push_back(cmd);
}
break; //CD
}
case 3: now->sz(); break; //SZ
case 4: now->ls(); break; //LS
case 5: now->tree(); break; //TREE
case 6: {
bool success = false;
while (!success && !cmdList.empty())
{
cmd = cmdList.back();
cmdList.pop_back();
switch (cmd->type)
{
case 0: {Directory *tempp= now->rm(cmd->arg); if(tempp!=NULL) success=true;break; }//UNDO MAKDIR
case 1: success = now->addChild(cmd->temp); break;//UNDO rm;
case 2: now = cmd->temp; success = true; break; //UNDO CD
}
}
if (success)
cout<<"OK"<<endl;
else
cout<<"ERR"<<endl;
break;
}
}
}
}
int main()
{
int T;
cin >> T;
for (int i = 0; i < T; i++)
{
solve();
}
return 0;
}