UVa12298 3KP-BASH Project

UVa12298 3KP-BASH Project

  • 题目链接
  • 题意
    • 输入格式
    • 输出格式
  • 分析
  • AC 代码

题目链接

  UVa12298 3KP-BASH Project

题意

  摘自 《算法竞赛入门经典:训练指南》刘汝佳,陈锋著。有删改。

  你的任务是为一个假想的 3KP 操作系统编写一个简单的 Bash 模拟器。由于操作系统本身还没有写完,你暂时需要在模拟器的内存中虚拟出一个文件系统,而不是和真实的文件系统交互。下面介绍 3KP 操作系统中的文件系统。
  文件(file)是数据存储的最小单位。文件名由英文字母(大小写敏感)、数字和点(.)组成,但不能包含两个连续的点。用户创建的文件名不能是单个的点字符(它代表当前目录)。文件名长度不能超过 255,文件大小不能超过 2 63 2^{63} 263。一个文件可能具有 directory 属性和 hidden 属性。
  目录(directory)是大小为 0 、具有 directory 属性的文件,里面可以保存任意数量的目录和文件。空文件系统只有一个文件,叫做“根目录”。在任意时刻,有一个称为“当前目录”的目录。 Bash 启动时,当前目录就是根目录。
  指令中引用文件的时候,可以使用绝对路径或者相对路径。绝对路径以字符/开头,如 /home/acm/uva ;相对路径不以字符 / 开头,当前目录和上层目录分别用 . 和 … 表示,如当前目录为 /home/acm/uva,那么相对路径 …/…/…/home/… 就是根目录。 你的 Bash 模拟器需要支持 8 个命令,具体如下表所示。

用法描述
cd path改变当前目录。path 可以是相对路径,也可以是绝对路径。如果目录不存在或名称不合法,输出 path not found 。
touch
filename
[-size]
[-h]
修改文件。文件名之前的部分应当是文件系统中存在的目录,否则输出 path not found 。filename 的最后一部分应该是合法的文件名,否则输出 bad usage 。在正常情况下,同名文件(如果有的话)应当先被删除,然后新建文件。文件的大小由 -size 参数给出(默认为0),参数-h表示创建隐藏文件。如果存在同名的目录,输出 a dretory with the same name exists 。用户不会指定多个 -size 参数。
mkdir
path [-h]
创建目录。文件名之前的部分应当是文件系统中存在的目录,否则输出 path not found。path 的最后一部分应该是合法的文件名,否则输出 bad usage。参数 -h 表示创建隐藏目录。如果存在一个同名目录或文件,输出 file or directory with the same name exists 。
find filename
[-r] [-h]
查找目录或文件。filename的前部应当是文件系统中存在的目录,否则输出 path not found。filename 的最后一部分应该是合法的文件名(否则按找不到处理),表示查找的目录或者文件的名称。-r 参数表示当前目录下的所有子目录也要查找。默认情况下,find 命令在显示结果时会忽略掉隐藏文件(但会在隐藏目录中进行查找), 如果加了 -h 参数,则会连隐藏文件一起显示。如果没有一个需要输出的文件,输出 file not found 。否则对于找到的每个文件,输出单独的一行,依次包含带绝对路径的文件名、文件大小,以及 hidden (如果有隐藏属性)、 dir (如果是目录)。这些文件应当按照带绝对路径的文件名的字典序从小到大输出。
ls [path]
[-h] [-r]
[-s] [-S]
[-f] [-d]
没有参数的情况下,Is 命令列出当前目录的所有非隐藏文件(格式同find)。如果没有一个需要输出的内容,输出 [empty]。如果path是文件系统中存在的目录,则列出该目录(而非当前目录)中的文件。如果指定了 path 參数但 path 不是一个合法的路径,则输出 path not found 。 -h 表示要列出隐藏文件(默认不显示),-r 表示递归列出所有子目录的文件(即使未指定 -h 也需要进入隐藏目录递归),-s 表示按照文件大小的非降序排列(文件大小相同的情况仍按照带绝对路径的文件名的字典序排列),-S 表示按照文件大小的非升序排列(如文件大小相同则参见 -s),-d 表示只列出目录,-f 表示只列出非目录。用户不会同时指定 -s 和 -S 。
pwd输出当前目录的绝对路径
exit退出 Bash
grep
“string”
本题中,grep 只能通过管道的形式调用,即 command1 | grep"string",表示先执行 command1,然后在命令输出结果中搜索字符串“string”,按照原顺序输出所有包含这个字符串的行。比如,前一个命令的输出是 path not found,那么 grep"ot fou"就应该也输出 path not found。用户输入的 string 参数保证在一对引号内,且内部不含引号,也没有转义符

  命令行包含一条或多条命令,用管道符号 | 隔开(管道符号的前后不一定有空格)。第一条命令必须是除了 grep 之外的上述命令之一,而剩下的命令必须是 grep。如果违反上述规定,应输出 bad usage,并且不执行任何命令。输入不会在除了 grep 之外的其他命令中使用引号(原题未写,但书中是这么说的)。
  命令和参数、参数和参数之间用一个或多个空格隔开。每个命令的必选参数(如果有的话)不一定是第一个参数,可选参数可能在必选参数之前,且以任意顺序出现。如果用户忽略了必选参数,或传入了过多必选参数,应输出 bad usage 。但忽略传入的无效可选参数(如 mkdir a -h -h -h -h -abababab 等价于 mkdir a -h)。

输入格式

  输入包含多组数据,每组数据对应一次独立的 BASH 会话。数据的每行为一行命令(长度不超过 2048 个字符)。命令的参数之间可以有任意数量的空白字符。每次会话的最后一条命令保证为exit。输入结束标志为文件结束符(EOF)。假定每次会话开始时,文件系统为空,当前目录为根目录。

输出格式

  依次输出每行命令的输出(如果有的话)。

分析

  纯模拟题,很难 AC,洛谷题解上 ExplodingKonjac 说他耗时 346 天通过此题。贴上 ExplodingKonjac 的补充说明:

  文件名中只能包含大小写英文字母、数字、点号,且不可以是单个点号,也不能包含两个连续点号,长度不能超过 255。这句在题面中有,但是容易被忽视。执行 touch 和 mkdir 时文件或目录名违反上述规定要输出 bad usage(但如果文件文件或目录名之前的路径如果不存在要输出 path not found)。
  文件大小不能超过 2 63 2^{63} 263,违反该规定应当输出 bad usage。实测数据没有超过 2 63 2^{63} 263的,不过读取-size参数应该注意像 -100ch 这种要当数字提取成100,而 -ch100这种不能当数字。
  文件路径中两个斜杠之间可能是空的,这种东西表示当前路径。比如路径 .///././// 和路径 . 表示同一种东西。
  一个命令行中有由管道符号隔开的多个命令时,应该首先判断第一个命令不是 grep(是 grep 则直接输出 bad usage)才执行第一个命令。之后的命令必须都是 grep,否则直接输出 bad usage 而不执行第二个命令及以后的所有命令。 比如 mkdir a | tt | grep "cc"会先执行 mkdir a,然后输出 bad usage。
  管道符号和空格可能被引号所包含,你不能根据其分隔参数或命令。比如说,grep “ot fou” 不能被分隔成 grep、“ot、fou”。
  ls 命令中可能同时包含 -d 和 -f 参数,此时应该输出 [empty]。但是数据中不会同时包含 -s 和 -S。

AC 代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;#define ULL unsigned long longconst string no_such_command = "no such command", bad_usage = "bad usage", path_not_found = "path not found",file_not_found = "file not found", dir_exists = "a directory with the same name exists",f_dir_exists = "file or directory with the same name exists", empty_l = "[empty]";
struct params {ULL size = 0; bool r = false, h = false, s = false, S = false, f = false, d = false;};
struct file {string name, full_path; int parent; ULL size; bool dir, hidden; vector<int> sub;file(const string& name, const string& full_path, int parent, ULL size = 0, bool dir = false, bool hidden = false):name(name), full_path(full_path), parent(parent), size(size), dir(dir), hidden(hidden) {};
};
vector<file> fs; int curr_dir;void trim(string& s) {int l, r, t = s.size();for(l=0; l<t; ++l) if(!isspace(s[l])) break;for(r=t-1; r>l; --r) if(!isspace(s[r])) break;s = s.substr(l, r-l+1);
}bool valid_path(const string& s, int& dir, string& name, bool pre = true) {vector<string> p; dir = s[0] == '/' ? 0 : curr_dir;if (!s.empty() && s.back() == '/') pre = false;for (int i=0, h=0, t=s.size(); i<t; ++i) {if (s[i] == '/') {if (i > 0 && s[i-1] != '/') p.push_back(s.substr(h, i-h));h = i+1;} else if (i+1 == t) p.push_back(s.substr(h, t-h));}for (int i=0, t=p.size()-pre; i<t; ++i) {if (p[i] == ".") continue;if (p[i] == "..") {if (fs[dir].parent < 0) return false;dir = fs[dir].parent;} else {const vector<int>& sub = fs[dir].sub; bool ok = false;for (int j=sub.size()-1; j>=0; --j) if (fs[sub[j]].name == p[i]) {if (fs[sub[j]].dir) dir = sub[j], ok = true;break;}if (!ok) return false;}}name = p.empty() || s.back() == '/' ? "" : p.back();return true;
}bool valid_name(const string& s) {if (s.empty() || s.size() > 255) return false;for (int i=0, t=s.size(); i<t; ++i)if ((s[i]=='.' && (t==1 || (s[i+1]=='.'))) || (s[i]!='.' && !isalpha(s[i]) && !isdigit(s[i]))) return false;return true;
}bool cmp_p(int i, int j) {return fs[i].full_path < fs[j].full_path;
}bool cmp_s(int i, int j) {return fs[i].size < fs[j].size || (fs[i].size == fs[j].size && fs[i].full_path < fs[j].full_path);
}bool cmp_S(int i, int j) {return fs[i].size > fs[j].size || (fs[i].size == fs[j].size && fs[i].full_path < fs[j].full_path);
}void visit_fs(int dir, vector<int>& out, const string& name, bool r, bool h, bool d = true, bool f = true) {for (int i=0, t=fs[dir].sub.size(); i<t; ++i) {const file& fi = fs[fs[dir].sub[i]];if ((!fi.hidden || h) && ((fi.dir && d) || (!fi.dir && f)) && (name.empty() || fi.name == name))out.push_back(fs[dir].sub[i]);if (fi.dir && r) visit_fs(fs[dir].sub[i], out, name, r, h, d, f);}
}string info(const file& f) {ostringstream ss; ss << f.full_path << ' ' << f.size;if (f.hidden) ss << " hidden";if (f.dir) ss << " dir";return ss.str();
}void new_session() {fs.clear(); fs.push_back(file("", "/", -1, 0, true)); curr_dir = 0;
}void cd(const string& mp, vector<string>& out) {int dir; string name;if (!valid_path(mp, dir, name, false)) return out.push_back(path_not_found);curr_dir = dir;
}void touch(const string& mp, const params& p, vector<string>& out) {int dir; string name;if (!valid_path(mp, dir, name)) return out.push_back(path_not_found);if (!valid_name(name)) return out.push_back(bad_usage);for (int i=fs[dir].sub.size()-1; i>=0; --i) if (fs[fs[dir].sub[i]].name == name) {if (fs[fs[dir].sub[i]].dir) return out.push_back(dir_exists);fs[fs[dir].sub[i]].size = p.size; fs[fs[dir].sub[i]].hidden = p.h;return;}file fi(name, (dir ? fs[dir].full_path + '/' : "/") + name, dir, p.size, false, p.h);int s = fs.size(); fs[dir].sub.push_back(s); fs.push_back(fi);
}void mkdir(const string& mp, const params& p, vector<string>& out) {int dir; string name;if (!valid_path(mp, dir, name)) return out.push_back(path_not_found);if (!valid_name(name)) return out.push_back(bad_usage);for (int i=fs[dir].sub.size()-1; i>=0; --i) if (fs[fs[dir].sub[i]].name == name)return out.push_back(f_dir_exists);file fi(name, (dir ? fs[dir].full_path + '/' : "/") + name, dir, 0, true, p.h);int s = fs.size(); fs[dir].sub.push_back(s); fs.push_back(fi);
}void find(const string& mp, const params& p, vector<string>& out) {int dir; string name;if (!valid_path(mp, dir, name)) return out.push_back(path_not_found);vector<int> res;visit_fs(dir, res, name, p.r, p.h);if (res.empty()) return out.push_back(file_not_found);sort(res.begin(), res.end(), cmp_p);for (int i=0, t=res.size(); i<t; ++i) out.push_back(info(fs[res[i]]));
}void ls(const string& mp, const params& p, vector<string>& out) {int dir; string name;if (!valid_path(mp, dir, name, false)) return out.push_back(path_not_found);vector<int> res;visit_fs(dir, res, "", p.r, p.h, !p.f, !p.d);if (res.empty()) return out.push_back(empty_l);sort(res.begin(), res.end(), p.s ? cmp_s : (p.S ? cmp_S : cmp_p));for (int i=0, t=res.size(); i<t; ++i) out.push_back(info(fs[res[i]]));
}void run_command(const string& cmd, vector<string>& out) {if (cmd.empty()) return;istringstream ss(cmd); string s; ss >> s;if (s != "cd" && s != "touch" && s!= "mkdir" && s != "find" && s != "ls" && s != "pwd" && s != "exit" && s != "grep") {out.push_back(no_such_command);} else {string mp; params p; ULL v; string s1; ss >> s1;while (!s1.empty()) {if (s1[0] == '-') {if (isalpha(s1[1])) {if (s1[1] == 'r') p.r = true;else if (s1[1] == 'h') p.h = true;else if (s1[1] == 'd') p.d = true;else if (s1[1] == 'f') p.f = true;else if (s1[1] == 's') p.s = true;else if (s1[1] == 'S') p.S = true;} else if (istringstream(s1.substr(1)) >> v) p.size = v;else return out.push_back(bad_usage);} else if (!mp.empty()) return out.push_back(bad_usage);else mp = s1;s1.clear(); ss >> s1;}if (s == "cd") mp.empty() ? out.push_back(bad_usage) : cd(mp, out);else if (s == "touch") mp.empty() ? out.push_back(bad_usage) : touch(mp, p, out);else if (s == "mkdir") mp.empty() ? out.push_back(bad_usage) : mkdir(mp, p, out);else if (s == "find") mp.empty() ? out.push_back(bad_usage) : find(mp, p, out);else if (s == "ls") ls(mp, p, out);else if (s == "pwd") out.push_back(mp.empty() ? fs[curr_dir].full_path : bad_usage);else if (s == "grep" || (s == "exit" && !mp.empty())) out.push_back(bad_usage);else new_session();}
}void run_cmdline(const string& line) {vector<string> cmds, out;for (int i=0, t=line.size(), s=0, q=0; i<=t; ++i) if (i==t || (line[i] == '|' && !q)) {cmds.push_back(string(line.begin()+s, line.begin()+i)); s = i+1;} else if (line[i] == '"') q ^= 1;if (cmds.empty()) return;run_command(cmds[0], out);for (int i=1, t=cmds.size(); i<t; ++i) {istringstream ss(cmds[i]); string s, p; ss >> s; getline(ss, p); trim(p);if (s != "grep" || p.size() < 2 || p[0] != '"' || p.back() != '"') {cout << bad_usage << endl;return;}if (out.empty() || p.size()==2) continue;vector<string> filtered; s = p.substr(1, p.size()-2);for (int j=0, k=out.size(); j<k; ++j) if (out[j].find(s) != string::npos) filtered.push_back(out[j]);out.swap(filtered);}for (int i=0, t=out.size(); i<t; ++i) cout << out[i] << endl;
}int main() {string line; new_session();while (getline(cin, line)) run_cmdline(line);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/84987.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云打包生成的ipa上传构建版本经验分享

在上架ios应用&#xff0c;在苹果开发者中心操作的时候&#xff0c;需要提供一个构建版本&#xff0c;如下图所示&#xff1a; 点击蓝色加号&#xff0c;添加构建版本&#xff0c;但是点击蓝色加号后&#xff0c;并没有构建版本可以选。 原因是需要下载下面它推荐的工具来上传…

ESP32的spi通讯(Arduino)

目录 一.基本配置 1.esp32-wroom-32引脚图 2.接线方式 3.Arduino芯片选择和库文件 3.1Arduino配置&#xff08;2.0.11&#xff09; 3.2 下载ESP32SPISlave库&#xff08;0.6.8&#xff09;文件 二、代码编写 1.主机代码 2.从机代码 3.注意事项 三、运行效果 一.基本…

Spring-rabbit重试消费源码分析

在集成RabbitMQ与Spring Boot 3.1.x时&#xff0c;RetryOperationsInterceptor 是实现消息重试机制的关键组件。这里将深入分析 RetryOperationsInterceptor 的工作原理&#xff0c;尤其是在消费者消费失败时的行为&#xff0c;并结合底层源码进行详解。 一、配置解析 首先&a…

如何使用JacksonTypeHandler处理mysql json字符串转List对象的问题

在使用mysql5.7或更高版本时&#xff0c;json类型字段应用场景越来越多&#xff0c;对于普通的对象或者List<Integer>、List<String>这些基础类型&#xff0c;jacksonTypeHandler都能很好的处理&#xff0c;如下&#xff1a; 1、定义一个person对象 import com.f…

华为云Flexus+DeepSeek征文 | 基于Dify构建股票分析助手

华为云FlexusDeepSeek征文 | 基于Dify构建AI 图片生成应用 一、构建股票分析助手前言二、构建股票分析助手环境2.1 基于FlexusX实例的Dify平台2.2 基于MaaS的模型API商用服务 三、构建股票分析助手实战3.1 配置Dify环境3.2 配置Dify工具3.3 创建股票分析助手3.4 使用股票分析助…

【0.1 漫画计算机组成原理】

🖥️ 漫画计算机组成原理 🎯 学习目标:深入理解计算机硬件基础,为后续Java编程和性能优化打下坚实基础 📋 目录 CPU架构与指令集内存层次结构冯诺依曼架构与哈佛架构总线系统与IO设备计算机性能分析实际应用场景🎭 漫画引言 小明: “为什么我的Java程序有时候跑得飞…

pytorch 实战二 CNN手写数字识别

系列文章目录 文章目录 系列文章目录前言一、torchvision.datasets1. 数据下载2. 数据分批次传入 二、网络1. 网络搭建2. 训练3.测试 完整代码三、保存模型与推理&#xff08;inference&#xff09;模型保存推理鸣谢 前言 手写数字识别&#xff0c;就是要根据手写的数字0~9&…

[Godot] C#读取CSV表格创建双层字典实现本地化

最近研究了一下本地化&#xff0c;给大家用简单易懂的方式说明我是怎么实现的&#xff0c;使用CSV表格填写翻译&#xff0c;然后在Godot中读取为字典 表格填写 首先&#xff0c;我们表格可以按照下面这种格式填写 idzhenjaruesdefrapple苹果appleリンゴяблокоmanzanaA…

Spark 之 Subquery

各类 Subquery src/main/scala/org/apache/spark/sql/catalyst/expressions/predicates.scala /*** Evaluates to `true` if `values` are returned in `query`s result set.*/ case class InSubquery(values: Seq[Expression], query: ListQuery)extends Predicate with Une…

3.1.3_栈的链式存储实现

知识总览&#xff1a; 链栈定义&#xff1a; 头插法建立单链表&#xff1a; 每次要插入一个元素的时候&#xff0c;总是把该元素插在头节点之后的位置&#xff0c;如果规定只能在单链表的链头一端进行操作即为进栈操作 每次删除一个元素的时候&#xff0c;规定只能在单链表…

华为OD机试_2025 B卷_字符串重新排列(Python,100分)(附详细解题思路)

题目描述 给定一个字符串s&#xff0c;s包括以空格分隔的若干个单词&#xff0c;请对s进行如下处理后输出&#xff1a; 1、单词内部调整&#xff1a;对每个单词字母重新按字典序排序 2、单词间顺序调整&#xff1a; 1&#xff09;统计每个单词出现的次数&#xff0c;并按次数降…

http的缓存问题

一句话概括&#xff1a;浏览器请求资源的时候&#xff0c;会首先检查本地是否有缓存&#xff0c;减少向服务器请求的次数 一、缓存类型&#xff1a; 1. 强缓存&#xff08;本地缓存&#xff09;&#xff1a;直接读本地&#xff0c;不发请求 控制方式&#xff1a; ① Cache-C…

【网络安全】SRC漏洞挖掘思路/手法分享

文章目录 Tip1Tip2Tip3Tip4Tip5Tip6Tip7Tip8Tip9Tip10Tip11Tip12Tip13Tip14Tip15Tip16Tip17Tip18Tip19Tip20Tip21Tip22Tip23Tip24Tip25Tip26Tip27Tip28Tip29Tip30Tip1 “复制该主机所有 URL”:包含该主机上的所有接口等资源。 “复制此主机里的链接”:包括该主机加载的第三…

「Linux中Shell命令」Shell常见命令

知识点及案例解析 1. who 命令 功能:显示当前登录系统的用户信息,包括用户名、终端、登录时间、IP等。 案例: who输出示例: root tty1 2025-06-13 19:42 root pts/0 2025-06-13 19:45 (192.168.226.1)解析: 显示两个用户登录信息: 第一列(用…

StampedLock入门教程

文章目录 一、理解“戳” (Stamp)二、为什么 StampedLock 能提高读性能&#xff1f;秘密在于“乐观读”StampedLock性能对比性能对比结果图 总结 StampedLock完整演示代码对代码的疑问之处问题一&#xff1a;为什么 demonstrateOptimisticReadFailure 中写线程能修改成功&#…

基于云计算的振动弦分析:谐波可视化与波动方程参数理解-AI云计算数值分析和代码验证

振动弦方程是一个基础的偏微分方程&#xff0c;它描述了弹性弦的横向振动。其应用范围广泛&#xff0c;不仅可用于模拟乐器和一般的波动现象&#xff0c;更是数学物理以及深奥的弦理论中的重要基石。 ☁️AI云计算数值分析和代码验证 振动弦方程是描述固定两端弹性弦横向振动的…

Qt .pro配置gcc相关命令(三):-W1、-L、-rpath和-rpath-link

目录 1.Linux 动态库相关知识 1.1.动态库查找路径 1.2.查看程序依赖的动态库 1.3.修改动态库查找路径的方法 1.4.动态链接器缓存管理 2.-Wl参数 3.-L选项&#xff08;编译时路径&#xff09; 4.-rpath参数(运行时路径) 5.-rpath-link 参数 6.常见问题与解决方案 7.总…

Hoppscotch

官方地址 xixiaxiazxiaxix下载 • Hoppscotch Hoppscotch 是一款轻量级、基于 Web 的 API 开发套件&#xff0c;其核心功能和特点如下&#xff1a; 核心功能3 交互式 API 测试&#xff1a;允许用户实时发送请求并查看响应&#xff0c;方便记录 API 行为&#xff0c;在记录响…

RabbitMQ 知识详解(Java版)

RabbitMQ 知识详解&#xff08;Java版&#xff09; RabbitMQ 是一个开源的消息代理&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。它用于在分布式系统中实现应用解耦、异步通信和流量削峰。 核心概念 生产者(Producer)&#xff1a;发送消息的应用消费者(…

Flink task、Operator 和 UDF 之间的关系

要真正驾驭 Flink 并构建出高效、稳定、可扩展的流处理应用&#xff0c;仅仅停留在 API 的表面使用是远远不够的。深入理解其内部的运行机制&#xff0c;洞悉数据从代码到分布式执行的完整生命周期&#xff0c;以及明晰各个核心组件之间错综复杂而又协同工作的关系&#xff0c;…