题意
思路
1)自顶向下地设计整个程序框架,从程序的入口开始,先设计主函数,然后设计程序框架
程序入口
int main(){
int T;cin>>T;
while(T--)solve();
return 0;
}
void solve(){
int n;cin>>n;
while(n--){
cin>>temps;
...
}
}
一条命令不单纯是字符串,它可能设计undo,考虑封装成Command,以便服用和拓展
while(n--){
scanf("%s",tmps);
Command* cmd=new Command(tmmps);
}
2)思考封装
考虑“有什么”和“做什么”。
比如刚刚我们有一个命令字符串“tmps”,它即代表一条命令。
考虑到一条命令不单有命令形式,还有命令参数,比如“MKDIR s”,等
会我们肯定还要进行参数的分离,同类信息最好内聚,所以 —— 封装之!
封装Command
struct Command
{
int type;//命令的类型
string arg;//命令的 参数
Command(string s) {//构造函数
....
}
};
将魔法值改成常量设计:
const string CMDNAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
构造函数:
Command(string s) {//构造函数
for(int i=0;i<7;i++)
if (CMDNAMES[i] == s) {
type = i;
//MKDIR、RM、CD的参数后续读入
if (i < 3) scanf("%s", tmps), arg = tmps;
return;
}
}
3)树形结构
维护一棵目录树是实现这道题的必然需求 —— 如何维护一棵树
题目要求一个目录能够根据子目录的名字取到它的子目录
所以要用 map<string,目录>,它可以根据 key 也就是 string 在内部进行排序。这样每次可以 log 级别复杂度取到子目录
struct Directory
{
string name;//当前目录的名字
map<string, Directory*>children;//用指针是避免复制构造造成的内存浪费
Directory* parent;//以备CD.. 返回上级目录
int subtreeSize;//以备sz要输出子树大小
Directory(string name, Directory* parent) {
this->name = name;
this->parent = parent;
this->subtreeSize = 1;
}
};
4)解题框架
要开始写具体实现的时候,你发现,对于每条指令要有一个执行对象,那就是当前目录 “now
对于每个具体操作假设已经完成,实现solve胡基本框架:
void solve(){
int n; cin >> n;
Directory *now = new Directory("root", NULL);
while (n--) {
scanf("%s", tmps);
Command* cmd = new Command(tmps);
switch (cmd->type)
{
case 0:now->mkdir(cmd->arg) //MKDIR
case 1:now->rm(cmd->arg)//RM
case 2:now->cd(cmd->arg)//CD
case 3:now->sz();//SZ;
case 4:now->ls();//LS;
case 5:now->tree();//TREE
case 6://undo
}
cout<<endl;//题目要求不同组数据直接要输出换行
}
5)实现细节:
对于结构体Dictionary:
struct Directory
{
Directory* mkdir(string name);
Directory* rm(string name);
Directory* cd(string name);
void sz();
void ls();
void tree();
}
同时你发现两个问题,这时发现两个问题。第一,要设计“返回值”告知某条命令的执行的结果成功/失败。 第二,“UNDO”没法封装在某一个 Directory 内部,它是隶属于当前测试数据环境的。所以,对于执行成功的命令,要保存起来,以备“UNDO。UNDO命令,它必须是 MKDIR、RM、CD 三种之一,而且需要已执行成功。因此,要保存每条指令执行的执行结果,保存到 struct Command{…}。
Directory* tmpDir;//记录刚刚操作涉及的目录节点
void solve(){
int n; cin >> n;
Directory *now = new Directory("root", NULL);
vector<Command*>cmdList;//新增加的数组存成功执行的命令以备undo
while (n--) {
scanf("%s", tmps);
Command* cmd = new Command(tmps);
Directory * ch;
switch (cmd->type)
{
case 0:case 1://MKDIR、RM
cmd->tmpDir = cmd->type == 0 ? now->mkdir(cmd->arg) : now->rm(cmd->arg);
if (cmd->tmpDir == nullptr) printf("ERR\n");
else {
printf("OK\n");
cmdList.push_back(cmd);
}
break;
case 2://CD
ch = now->cd(cmd->arg);
if (ch == nullptr)printf("ERR\n");
else {
printf("OK\n");
cmd->tmpDir = now;
now = ch;
cmdList.push_back(cmd);
}
break;
case 3:now->sz(); break;
case 4:now->ls(); break;
case 5:now->tree(); break;
//15、解决undo
case 6://undo
}
}
cout<<endl;
}
UNDO操作的实现:
case 6://undo
bool success = false;//undo执行成功与否
while (!success && !cmdList.empty()) {
cmd = cmdList.back(); cmdList.pop_back();
switch (cmd->type)
{
//UNDO MKDIR
case 0:success = now->rm(cmd->arg) != nullptr; break;
//UNDO RM
case 1:success = now->addChild(cmd->tmpDir); break;
//UNDO CD
case 2:now = cmd->tmpDir; success = true; break;
}
}
printf(success ? "OK\n" : "ERR\n");
}
6)目录管理实现细节:
struct Dictionary//目录
{
string name;
map<string,Dictionary*> children;//孩子按照字典序
Dictionary* parent;
int subtreeSize;
vector<string> tenDescendants[100];
bool updated;
Dictionary(string n,Dictionary* p)
{
name = n;
parent = p;
subtreeSize = 1;
}
Dictionary* getChild(string name)
{
map<string,Dictionary*>::iterator it = children.find(name);
if(it == children.end())
return NULL;
return it->second;
}
Dictionary* mkdir(string name)
{
if(children.find(name)!=children.end())
return NULL;
Dictionary* ch = new Dictionary(name,this);
children[name] = ch;//创建
maintain(+1);
return ch;
}
Dictionary* rm(string name)
{
map<string,Dictionary*>::iterator it = children.find(name);
if(it == children.end())
return NULL;
maintain(-1*it->second->subtreeSize);
children.erase(it);
return it->second;
}
Dictionary* cd(string name)
{
if(".." ==name)
return this->parent;
return getChild(name);
}
bool addChild(Dictionary* ch)
{
if(children.find(ch->name)!=children.end())
return false;
children[ch->name] = ch;
maintain(+ch->subtreeSize);
return true;
}
void maintain(int delta)
{
updated = true;
subtreeSize+=delta;
if(parent!=NULL)
parent->maintain(delta);//递归向上更新
}
void sz()
{
printf("%d\n",this->subtreeSize);
}
void ls()
{
int sz = children.size();
if(sz==0) printf("EMPTY\n");
else if(sz<=10)
{
for(map<string,Dictionary*>::iterator it = children.begin();it!=children.end();it++)
printf("%s\n",it->first.c_str());
}
else
{
map<string,Dictionary*>::iterator it = children.begin();
for(int i = 0;i<5;i++,it++) printf("%s\n",it->first.c_str());
printf("...\n");
it = children.end();
for(int i = 0;i<5;i++) it--;
for(int i=0;i<5;i++,it++) printf("%s\n",it->first.c_str());
}
}
};
7)实现TREE命令
利用缓存(懒更新),节点数远少于 TREE 操作数,指不定还有重复询问,对于目录相同期间问过的相同问题,理应只有一次是计算过程
void tree()
{
if(subtreeSize==1) printf("EMPTY\n");
else if(subtreeSize<=10)
{
if(this->updated)
{
tenDescendants->clear();
treeAll(tenDescendants);
this->updated = false;
}
for(int i = 0;i<subtreeSize;i++) printf("%s\n",tenDescendants->at(i).c_str());
}
else
{
if(this->updated)
{
tenDescendants->clear();
treeFirstSome(5,tenDescendants);
treeLastSome(5,tenDescendants);
this->updated = false;
}
for(int i=0;i<5;i++) printf("%s\n",tenDescendants->at(i).c_str());
printf("...\n");
for(int i=9;i>=5;i--) printf("%s\n",tenDescendants->at(i).c_str());
}
}
//更新全桶
void treeAll(vector<string>* bar)//前序遍历
{
bar->push_back(name);
for(map<string,Dictionary*>::iterator it = children.begin();it!=children.end();it++)
it->second->treeAll(bar);
}//更新先根序列的5个
void treeFirstSome(int num,vector<string>* bar)
{
bar->push_back(name);num--;
if(num==0 ) return;
int n = children.size();
map<string,Dictionary*>::iterator it = children.begin();
while(n--)
{
int snum= it->second->subtreeSize;
if(snum>=num)
{
it->second->treeFirstSome(num,bar);
return;
}
else
{
it->second->treeFirstSome(snum,bar);
num -= snum;
}
it++;
}
}
//更新后根序列的5个
void treeLastSome(int num,vector<string>* bar)
{
int n = children.size();
map<string,Dictionary*>::iterator it = children.end();
while(n--)
{
it--;
int snum = it->second->subtreeSize;
if(snum>=num)
{
it->second->treeLastSome(num,bar);
return;
}
else
{
it->second->treeLastSome(snum,bar);
num-= snum;
}
}
bar->push_back(name);
}
完整代码
#include <bits/stdc++.h>
using namespace std;
char tmps[20];
struct Dictionary//目录
{
string name;
map<string,Dictionary*> children;//孩子按照字典序
Dictionary* parent;
int subtreeSize;
vector<string> tenDescendants[100];
bool updated;
Dictionary(string n,Dictionary* p)
{
name = n;
parent = p;
subtreeSize = 1;
}
Dictionary* getChild(string name)
{
map<string,Dictionary*>::iterator it = children.find(name);
if(it == children.end())
return NULL;
return it->second;
}
Dictionary* mkdir(string name)
{
if(children.find(name)!=children.end())
return NULL;
Dictionary* ch = new Dictionary(name,this);
children[name] = ch;//创建
maintain(+1);
return ch;
}
Dictionary* rm(string name)
{
map<string,Dictionary*>::iterator it = children.find(name);
if(it == children.end())
return NULL;
maintain(-1*it->second->subtreeSize);
children.erase(it);
return it->second;
}
Dictionary* cd(string name)
{
if(".." ==name)
return this->parent;
return getChild(name);
}
bool addChild(Dictionary* ch)
{
if(children.find(ch->name)!=children.end())
return false;
children[ch->name] = ch;
maintain(+ch->subtreeSize);
return true;
}
void maintain(int delta)
{
updated = true;
subtreeSize+=delta;
if(parent!=NULL)
parent->maintain(delta);//递归向上更新
}
void sz()
{
printf("%d\n",this->subtreeSize);
}
void ls()
{
int sz = children.size();
if(sz==0) printf("EMPTY\n");
else if(sz<=10)
{
for(map<string,Dictionary*>::iterator it = children.begin();it!=children.end();it++)
printf("%s\n",it->first.c_str());
}
else
{
map<string,Dictionary*>::iterator it = children.begin();
for(int i = 0;i<5;i++,it++) printf("%s\n",it->first.c_str());
printf("...\n");
it = children.end();
for(int i = 0;i<5;i++) it--;
for(int i=0;i<5;i++,it++) printf("%s\n",it->first.c_str());
}
}
void tree()
{
if(subtreeSize==1) printf("EMPTY\n");
else if(subtreeSize<=10)
{
if(this->updated)
{
tenDescendants->clear();
treeAll(tenDescendants);
this->updated = false;
}
for(int i = 0;i<subtreeSize;i++) printf("%s\n",tenDescendants->at(i).c_str());
}
else
{
if(this->updated)
{
tenDescendants->clear();
treeFirstSome(5,tenDescendants);
treeLastSome(5,tenDescendants);
this->updated = false;
}
for(int i=0;i<5;i++) printf("%s\n",tenDescendants->at(i).c_str());
printf("...\n");
for(int i=9;i>=5;i--) printf("%s\n",tenDescendants->at(i).c_str());
}
}
void treeAll(vector<string>* bar)//前序遍历
{
bar->push_back(name);
for(map<string,Dictionary*>::iterator it = children.begin();it!=children.end();it++)
it->second->treeAll(bar);
}
void treeFirstSome(int num,vector<string>* bar)
{
bar->push_back(name);num--;
if(num==0 ) return;
int n = children.size();
map<string,Dictionary*>::iterator it = children.begin();
while(n--)
{
int snum= it->second->subtreeSize;
if(snum>=num)
{
it->second->treeFirstSome(num,bar);
return;
}
else
{
it->second->treeFirstSome(snum,bar);
num -= snum;
}
it++;
}
}
void treeLastSome(int num,vector<string>* bar)
{
int n = children.size();
map<string,Dictionary*>::iterator it = children.end();
while(n--)
{
it--;
int snum = it->second->subtreeSize;
if(snum>=num)
{
it->second->treeLastSome(num,bar);
return;
}
else
{
it->second->treeLastSome(snum,bar);
num-= snum;
}
}
bar->push_back(name);
}
};
struct Command
{
string com[7] = {"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
int type; //命令类型
string arg; //命令参数
Command(string s)
{
for(int i = 0;i<7;i++)
{
if(com[i]==s)
{
type = i;
if(i<3) scanf("%s",tmps),arg = tmps;
}
}
}
Dictionary* tmpDir;
};
void solve()
{ int n;scanf("%d",&n);
Dictionary* now = new Dictionary("root",NULL);//当前目录
vector<Command*> cmdList;
while(n--)
{
scanf("%s",tmps);
Command* cmd = new Command(tmps);
if(cmd->type==0||cmd->type==1)
{
cmd->tmpDir = cmd->type==0?now->mkdir(cmd->arg):now->rm(cmd->arg);
if(cmd->tmpDir == NULL) printf("ERR\n");
else
{
printf("OK\n");
cmdList.push_back(cmd);
}
}
if(cmd->type==2)
{
Dictionary* ch = now->cd(cmd->arg);
if(ch==NULL) printf("ERR\n");
else
{
printf("OK\n");
cmd->tmpDir = now;/
now = ch;
cmdList.push_back(cmd);
}
}
if(cmd->type==3) now->sz();
if(cmd->type==4) now->ls();
if(cmd->type==5) now->tree();
if(cmd->type==6)//undo
{
bool success = false;
while(!success&&!cmdList.empty())
{
cmd = cmdList.back();cmdList.pop_back();
switch(cmd->type)
{
case 0: success = now->rm(cmd->arg)!=NULL;break;
case 1: success = now->addChild(cmd->tmpDir);break;
case 2: now = cmd->tmpDir; success = true; break;
}
}
printf(success?"OK\n":"ERR\n");
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--) solve();
}