程序设计思维 week9 作业A-咕咕东的目录管理器

题目

咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!
初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:
在这里插入图片描述

Input

输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);

Ouput

每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。

Sample 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

Sample Ouput

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

思路

因为在每个子目录下,使用字典序排序,因此可以使用map记录子目录。map的first存子目录的名字,second存目录类型的指针。
目录结构体成员如下:

struct Directory{
    string name;
    map<string, Directory*> children;
    Directory* parent;
    int subtreeSize;
    vector<string>* tenDescendants;
    bool updated;
};

声明数据类型command,对输入的指令进行操作,成员包括指令类型、指令参数、目录。

struct command{
    const string COMMAND_NAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
    int type;
    string arg;
    Directory* tmpDir;
    command(string str){
        this->type = -1;
        for (int i = 0; i < 7; i++){
            if (COMMAND_NAMES[i] == str){
                this->type = i;
                if (i < 3){
                    cin >> tmpstr;
                    this->arg = tmpstr;
                }
                return;
            }
        }
    }
};

目录中还包含了题目要求的操作。其中,前三个操作并不是直接输出,而转移到了处理函数solve()中解决。因为这三个操作可能需要UNDO,因此不仅需要输出,还要返回UNDO必要的参数。
mkdir:则是在当前目录下创建一个新目录,所以我们需要一个指针now来指向当前目录,初始值为root。在创建成功之后,返回刚刚创建的目录的指针,如果指针不为空,则创建成功,将刚刚执行成功的这条指令加入到一个vector对象中, 该对象存储已经成功执行的命令。
rm:与mikdir类似。
cd:进入一个新目录,在操作成功后,更改now指针的指向。
sz,ls,tree:直接调用函数,不需要其他改变。
undo:则从vector中取出一条最近执行成功的指令,如果为mkdir,则将新建的目录删除,如果为rm,则将之前删除的目录加上,如果为cd,则返回上一级。
本题,最最关键的问题是时间限制和数据范围。命令总数1e5,也就是说TREE操作的数目极限情况下差不多要到1e5次,多组数据最多20组,显然如果每一次TREE操作都进行遍历的话会超时。
因此,采用懒更新的方法,在每次进行插入或删除操作时标记更新,然后在进行TREE操作时将答案存储。如果下次进行TREE操作没有更新,无需重新遍历,直接输出即可。

代码

#include<iostream>
#include<map>
#include<vector>
using namespace std;
string tmpstr;

struct Directory{
    string name;
    map<string, Directory*> children;
    Directory* parent;
    int subtreeSize;
    vector<string>* tenDescendants;
    bool updated;
    Directory(){
        tenDescendants= new vector<string>;
        this->updated = true;
        parent = new Directory();
    }
    Directory(string name, Directory* parent){
        this->name = name;
        this->parent = parent;
        this->subtreeSize = 1;
        tenDescendants= new vector<string>;
    }
    bool addChild(Directory* ch){
        if (children.find(ch->name) != children.end())
            return false;
        children[ch->name] = ch;
        maintain(+ch->subtreeSize);
        return true;
    }
    Directory* getChild(string name){
        auto it = children.find(name);
        if (it == children.end())
            return NULL;
        return it->second;
    }
    Directory* mkdir(string name){
        if (children.find(name) != children.end())
            return NULL;
        Directory* ch = new Directory(name, this);
        children[name] = ch;
        maintain(+1);
        return ch;
    }

    Directory* rm(string name){
        auto it = children.find(name);
        if (it == children.end())
            return NULL;
        maintain(-1 * it->second->subtreeSize);
        children.erase(it);
        return it->second;
    }

    Directory* cd(string name){
        if (".." == name)
            return this->parent;
        return getChild(name);
    }

    void maintain(int delta){
        updated = true;
        subtreeSize += delta;
        if (parent != NULL)
            parent->maintain(delta);
    }
    void sz(){
        cout << this->subtreeSize << endl;
    }
    void ls(){
        int sz = children.size();
        if (sz == 0)
            cout << "EMPTY" << endl;
        else if (sz <= 10)
            for (auto& entry : children)
                cout << entry.first << endl;
        else{
            auto it = children.begin();
            for (int i = 0; i < 5; i++, it++)
                cout << it->first << endl;
            cout << "..." << endl;
            it = children.end();
            for (int i = 0; i < 5; i++)
                it--;
            for (int i = 0; i < 5; i++, it++)
                cout << it->first << endl;
        }
    }
    void tree(){
        if (subtreeSize == 1)
            cout << "EMPTY" << endl;
        else if (subtreeSize <= 10){
            if (this->updated){
                tenDescendants->clear();
                treeAll(tenDescendants);
                this->updated = false;
            }
            for (int i = 0; i < subtreeSize; i++)
                cout << tenDescendants->at(i) << endl;
        }
        else{
            if (this->updated){
                tenDescendants->clear();
                treeFirst(5, tenDescendants);//前五个
                treeLast(5, tenDescendants);//后五个
                this->updated = false;
            }
            for (int i = 0; i < 5; i++)
                cout << tenDescendants->at(i).c_str() << endl;
            cout << "..." << endl;
            for (int i = 9; i >= 5; i--)
                cout << tenDescendants->at(i).c_str() << endl;
        }
    }

public:
    void treeAll(vector<string>* v){
        v->push_back(name);
        for (auto& entry : children)
            entry.second->treeAll(v);
    }

    void treeFirst(int num, vector<string>* v){
        v->push_back(name);
        if (--num == 0)
            return;
        int n = children.size();
        auto it = children.begin();
        while (n--){
            int sts = it->second->subtreeSize;
            if (sts >= num){
                it->second->treeFirst(num, v);
                return;
            }
            else{
                it->second->treeFirst(sts, v);
                num -= sts;
            }
            it++;
        }
    }

    void treeLast(int num, vector<string>* v){
        int n = children.size();
        auto it = children.end();
        while (n--){
            it--;
            int sts = it->second->subtreeSize;
            if (sts >= num){
                it->second->treeLast(num, v);
                return;
            }
            else{
                it->second->treeLast(sts, v);
                num -= sts;
            }
        }
        v->push_back(name);
        
    }
};


struct command{
    const string COMMAND_NAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
    int type;
    string arg;
    Directory* tmpDir;
    command(string str){
        this->type = -1;
        for (int i = 0; i < 7; i++){
            if (COMMAND_NAMES[i] == str){
                this->type = i;
                if (i < 3){
                    cin >> tmpstr;
                    this->arg = tmpstr;
                }
                return;
            }
        }
    }
};




void solve()
{
    int n;
    cin >> n;
    Directory* now = new Directory("root", NULL);
    vector<command*> cmdList;
    while (n--){
        cin >> tmpstr;
        command* cmd = new command(tmpstr);
        switch (cmd->type){
        case 0:{
            cmd->tmpDir = now->mkdir(cmd->arg);//MKDIR
            if (cmd->tmpDir == NULL)
                cout << "ERR" << endl;
            else{
                cout << "OK" << endl;
                cmdList.push_back(cmd);
            }
            break;
        }
        case 1:{
            cmd->tmpDir = now->rm(cmd->arg);//MKDIR
            if (cmd->tmpDir == NULL)
                cout << "ERR" << endl;
            else{
                cout << "OK" << endl;
                cmdList.push_back(cmd);
            }
            break;
        }
        case 2:{
            Directory* ch = now->cd(cmd->arg);
            if (ch == NULL)
                cout << "ERR" << endl;
            else{
                cout << "OK" << endl;
                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;
        case 6:{
            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;
                default:
                    break;
                }
            }
            cout << (success ? "OK" : "ERR") << endl;
            break;
        }
        default:
            break;
        }
    }
}

int main(){
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

题目链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值