题意:
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!
初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:
输入:
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
输出:
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
样例输入:
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
样例输出:
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
时空限制:
Time limit 6000 ms
Memory limit 1048576 kB
提醒:
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
思路:
整体上命令分为改变目录架构的“MKDIR” “RM” 操作,以及不改变架构的其它操作。因为"MKDIR" "RM"有数量限制,其它某些操作的复杂度可能比较大,因此考虑尽量完善"MKDIR"这些操作,保存一些其它操作需要的信息,降低其它操作的复杂度。
孩子目录的保存:
因为在新建目录以及删除目录的时候,需要根据目录名搜索子目录内是否存在重名的目录,所以需要字符串目录名作为索引,建立线性表。map自动按照索引排序,而且搜索的复杂度为O(logn)足够优秀。
SZ操作:
如果每一个SZ操作,重新遍历一遍,超时(上课时已经说明)
因此目录结点需要纪录sz,并且在makdir,和rm的时候,要更新本结点,以及父节点,祖先结点的sz,简化后,sz复杂度为O(1)
LS操作:
较简单的操作,遍历孩子目录结点就可以
UNDO操作:
此操作需要纪录前面进行的所有的改变目录架构的操作,“MKDIR”“RM”,以及CD操作
由此看来,最后把每一个命令作为一个结构体
结构体内部需要
1.将每种操作名称从string类型转化为int,便于后期switch_case选择
2.保留操作对象名称
3.保留UNDO操作需要返回的目录
{
MKDIR 需要存储新建目录的指针
RM 需要存储删除目录的指针
CD 需要存储原来的当前目录
}
将所有操作放在vector中,随着UNDO依次弹出尾部元素
若vector为空,则UNDO失败
如果弹出命令是"MKDIR",则逆操作就是删除新建的目录
如果弹出命令是"RM",则需要将原来删除的目录和当前目录重新连接起来
如果弹出命令是"CD",则需要将now变成原来的now
TREE操作:
如果sz > 10的时候,需要前序遍历前五个结点,以及后五个结点,遍历前五个结点复杂度倒不是太高,遍历后五个,则有可能耗费很长时间。
由于改变目录结构的操作"MKDIR""RM"小于5000,不改变目录结构的操作不影响TREE操作,因此可以采用vector存储前五个结点以及后五个结点,每个结点设置一个标志位,遇到MKDIR和RM操作,该结点以及祖先结点更新标志位设置为1.
只有更新标志位为1的时候,才重新遍历,选择元素。
其它情况,直接输出
上面的操作清楚之后,MKDIR,RM这些基础操作就好办了
MKDIR操作:
按字符串索引,检查是否有重名的子目录
如果没有重名的子目录,则更新当前目录的map,这样就建立了新子目录。
向上更新需要纪录的东西,如sz,更新标志位tag等
RM操作与之类似,也是先解决自己孩子的问题,然后向上反馈信息,更新
这些基础操作还需要存储操作的目录,这些命令信息也存储于一个vector 中,用于UNDO操作。
代码:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <cstdio>
using namespace std;
char tmp[20];
struct Directory;
struct Command{
const string Cmd_List[7] = {"MKDIR", "RM", "CD", "SZ", "LS", "TREE", "UNDO"};
int type;
string name;
Directory* targetDir;
Command(string t)
{
for (int i = 0; i < 7; i++)
{
if (Cmd_List[i] == t) type = i;
}
if(type >= 0 && type <= 2)
{
scanf("%s", tmp);
name = tmp;
}
}
};
struct Directory
{
Directory* par;
string name;
map<string, Directory*> child;
int sz;
vector<string>* tree_show;
bool tag;
Directory(string s, Directory* p)
{
name = s;
par = p;
sz = 1;
tag = true;
tree_show = new vector<string>;
}
Directory* makdir(string s);
Directory* rm(string s);
void update(int x);
Directory* cd(string s);
int Get_size()
{
return sz;
}
void ls();
void connect(Directory* d);
void tree();
void preAll(int num, Directory* d);
void postAll(int num, Directory* d);
};
Directory* Directory::makdir(string s)
{
map<string, Directory*>::iterator it = child.find(s);
if(it != child.end())return NULL;
Directory* d = new Directory(s, this);
child[s] = d;
update(1);
return d;
}
Directory* Directory::rm(string s)
{
map<string, Directory*>::iterator it = child.find(s);
if(it == child.end())return NULL;
Directory* d = (*it).second;
update(-1 * d->sz);
child.erase(it);
return d;
}
void Directory::connect(Directory* d)
{
child[d->name] = d;
update(d->sz);
}
void Directory::update(int x)
{
Directory* d = this;
while(d != NULL)
{
d->sz += x;
d->tag = true;
d = d->par;
}
}
Directory* Directory::cd(string s)
{
if(s == "..")
{
if(this->name != "root")
{
Directory* d = this->par;
return d;
}
else return NULL;
}
else{
map<string, Directory*>::iterator it = child.find(s);
if(it == child.end())return NULL;
else{
Directory* d = (*it).second;
return d;
}
}
}
void Directory::ls()
{
if (child.size() == 0)
{
printf("EMPTY\n");
return;
}
if(child.size() <= 10)
{
map<string, Directory*>::iterator it = child.begin();
for(; it != child.end(); it++)
{
printf("%s\n", (*it).first.c_str());
}
}
else{
map<string, Directory*>::iterator it = child.begin();
for(int i = 0; i < 5; i++,it++)
{
printf("%s\n", (*it).first.c_str());
}
printf("...\n");
it = child.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 Directory::tree()
{
if (sz == 1)
printf("EMPTY\n");
else {
if (sz <= 10)
{
if (tag)
{
tree_show->clear();
tag = false;
preAll(10, this);
}
for (int i = 0; i < tree_show->size(); i++)
printf("%s\n", tree_show->at(i).c_str());
}
else {
if (tag)
{
tree_show->clear();
tag = false;
preAll(5, this);
postAll(5, this);
}
for (int i = 0; i < 5; i++)
printf("%s\n", tree_show->at(i).c_str());
printf("...\n");
for (int i = 9; i >= 5; i--)
printf("%s\n", tree_show->at(i).c_str());
}
}
}
void Directory::preAll(int num , Directory* d)
{
int siz = num;
tree_show->push_back(d->name);
siz--;
if (siz == 0)return;
map<string, Directory*>::iterator it = d->child.begin();
for (; it != d->child.end(); it++)
{
if (siz <= (*it).second->sz)
{
preAll(siz, (*it).second);
return;
}
else {
siz = siz - (*it).second->sz;
preAll((*it).second->sz, (*it).second);
}
}
}
void Directory::postAll(int num, Directory* d)
{
int siz = num;
map<string, Directory*>::iterator it = d->child.end();
for (; it != d->child.begin(); )
{
it--;
if (siz <= (*it).second->sz)
{
postAll(siz, (*it).second);
return;
}
else {
postAll((*it).second->sz, (*it).second);
siz = siz - (*it).second->sz;
}
}
tree_show->push_back(d->name);
}
void solve()
{
Directory *now = new Directory("root", NULL);
int q;
scanf("%d", &q);
vector<Command> vec;
for(int i = 1; i <= q; i++)
{
scanf("%s", tmp);
string option = tmp;
Command C(option);
switch(C.type)
{
case 0:{
Directory* t = now->makdir(C.name);
C.targetDir = t;
if(t == NULL)printf("ERR\n");
else{
printf("OK\n");
vec.push_back(C);
}
break;
}
case 1:{
C.targetDir = now->rm(C.name);
if(C.targetDir == NULL)printf("ERR\n");
else{
printf("OK\n");
vec.push_back(C);
}
break;
}
case 2:{
C.targetDir = now;
Directory* d = now->cd(C.name);
if(d == NULL)printf("ERR\n");
else{
printf("OK\n");
vec.push_back(C);
now = d;
}
break;
}
case 3:{
printf("%d\n", now->Get_size());
break;
}
case 4: {
now->ls();
break;
}
case 5: {
now->tree();
break;
}
case 6: {
if (!vec.empty())
{
Command cc = vec.back();
vec.pop_back();
if (cc.type == 0)
now->rm(cc.name);
else if (cc.type == 1)
now->connect(cc.targetDir);
else if(cc.type == 2)
now = cc.targetDir;
printf("OK\n");
}
else
printf("ERR\n");
break;
}
}
}
}
int main()
{
int T;
cin >> T;
while(T--)
solve();
return 0;
}
总结:
1.懒更新:
在查询操作很多,影响结构的操作较少,并且存在重复查询的时候,采用懒更新的方法,用vector存储结果,只有使用了改变结构的操作,才重新遍历,更新vector,其它情况,直接输出vector元素。
2.scanf读取string :
定义char数组,scanf读取,然后再赋值给string
printf打印string:
使用c_str()函数将string 转换为c类型的char数组
3.先结构化设计整体框架,再追求细节
由于上课没太听懂“注入灵魂”的懒更新,略微参考了一位同学的博客,链接如下
https://blog.csdn.net/FoxMakarov/article/details/105658939