A - 咕咕东的目录管理器

题意:

咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值