题面描述:http://history.ccfccsp.org.cn/2020/0-tasks.pdf
这个题就是模仿Git实现一些简单文件操作功能:write、read、unlink、ls、commit、checkout、merge。
可以说按顺序,实现难度越来越高。后面的命令会影响前面命令的执行,所以为了得到后面测试点的分,前面写过的函数往往要做出很大的整改。
这是是我第一次写的大型模拟题。断断续续写了好多个小时,后面都是对着测试点的数据来写。现在还是cin和cout的输入输出,可能运行速度还是比较慢,但总归是答案都对上了。没有OJ可以测试,所以就先这样吧。由于开始没有特别认真,思路混乱就开始写了,导致调试起来也很慢。
总结一下经验:
1)面对大型模拟题,首先通读下整个题面,把需要注意的细节都标记出来。实际上它说什么,你就做什么基本就能得到正确答案,做完后再来想怎么减少时间复杂度。
2)在开始编程之前,把几个模块想好,要怎么划分。然后看测试点,按照测试点来对一个个功能进行实现,每做完一个测试点的模块就进行调试。如果这样有规律地执行应该能省很多时间。
3)把最简单的前半部分测试点实现之后,每多做一个测试点,都要记得备份一下,万一后面写糊涂了,把前面做好的搞没了就完蛋了。
4)特别注意使用的函数尽量不要用不熟悉的,如果非要用就必须对函数做些基本的测试,摸清这个函数对程序的影响。如果这里出了问题,在实际做题也找不到资料的话,基本上就卡死了。为了得分,笨笨地多写几行也比用不了解的函数要好的多。
现在我的观点是,这类题大概解决掉简单一些的测试点就可以跳过到下一题了,直到后面的题也到了难有进展的地步再回来死磕吧。心态真的很重要,要是头晕了就稍微歇一歇,活动一下身体。不舒服地继续做很可能会浪费许多时间。最后是,真的要自信,这种模拟题就是实现所有的细节,很累但不会说是很难。
#include <bits/stdc++.h>
using namespace std;
using RC = int;
#define OK 0
#define NOEXIT -1
#define DELETED -2
int static Time = 0;
class File {
public:
File(string filename)
: name(filename)
, del(false) {}
File(const File& file) {
name = file.name;
content = file.content;
del = file.del;
time = file.time;
}
~File(){}
void write(int offset, int len, string input) {
int l = content.length();
if (l < offset + len) content.resize(offset + len);
if (l <= offset) {
content.insert(l, string(offset - l, '.'));
content.resize(content.length() - (offset - l));
}
content.insert(offset, input);
content.resize(content.length() - input.length());
}
void read(int offset, int len, string& output) {
int l = content.length();
if (l < offset + len) {
int nl = offset + len;
content.resize(nl);
content.insert(l, string(nl - l, '.'));
content.resize(content.size() - (nl - l));
}
output = content.substr(offset, len);
}
void setRecover() {
del = false;
}
void setDel() {
del = true;
content.clear();
}
bool getDel() {return del;}
string getName() {return name;}
void setTime(int t) {time = t;}
int getTime() {return time;}
private:
string name;
string content;
bool del;
int time;
};
class Commit {
public:
Commit(Commit* fa = nullptr, Commit* me = nullptr)
: father(fa)
, merge(me)
, cmtname("uncommited") {}
~Commit(){
for (int i = 0; i < files.size(); i++) {
if (files[i] != nullptr)
delete files[i];
}
}
// uncommited无父节点, 在head里面找只要找最近的即可
RC query(string filename, File*& file) {
for (int i = 0; i < files.size(); i++) {
if (files[i]->getName() == filename) {
file = files[i];
if (files[i]->getDel()) return DELETED;
return OK;
}
}
RC rc1, rc2;
if (father != nullptr) {
rc1 = father->query(filename, file);
}
File* file2 = nullptr;
if (merge != nullptr) {
rc2 = merge->query(filename, file2);
}
if (file != nullptr && file2 != nullptr) {
if (file->getTime() < file2->getTime()) {
file = file2;
return rc2;
} else {
return rc1;
}
}
if (file != nullptr) return rc1;
if (file2 != nullptr) {
file = file2;
return rc2;
}
return NOEXIT;
}
// GeetFS中已经对其他情况做过处理,确保如果head中可以找到一定会放入uncommited中
void write(string filename, int offset, int len, const string& input) {
File* file = nullptr;
int rc = query(filename, file);
if (rc == NOEXIT) {
file = new File(filename);
files.push_back(file);
}
file->write(offset, len, input);
}
RC read(string filename, int offset, int len, string& output) {
File* file = nullptr;
RC rc = query(filename, file);
if (rc != OK) return rc;
file->read(offset, len, output);
return OK;
}
// 仅对uncommited会有unlink操作,因此不用考虑其他节点的影响
void unlink(const string& filename) {
File* file = nullptr;
RC rc = query(filename, file);
if (rc == OK) file->setDel();
}
void ls(vector<File>& tmpFile, map<string, int>& ID) {
for (int i = 0; i < files.size(); i++) {
if (!ID.count(files[i]->getName())) {
ID[files[i]->getName()] = tmpFile.size();
tmpFile.push_back(*files[i]);
} else {
int ind = ID[files[i]->getName()];
if (tmpFile[ind].getTime() < files[i]->getTime()) {
tmpFile[ind] = *files[i];
}
}
}
if (father != nullptr)
father->ls(tmpFile, ID);
if (merge != nullptr)
merge->ls(tmpFile, ID);
return;
}
void Copy(const File& file) {
files.push_back(new File(file));
}
void setName(const string& name) {
cmtname = name;
}
string getName() {
return cmtname;
}
void setParent(Commit* fa = nullptr, Commit* me = nullptr) {
father = fa;
merge = me;
}
bool isEmpty() {
return files.empty();
}
void setTime(int time) {
for (int i = 0; i < files.size(); i++)
files[i]->setTime(time);
}
private:
Commit* father;
Commit* merge;
vector<File*> files;
string cmtname;
};
class GeetFS {
public:
GeetFS()
: head(nullptr) {
uncommited = new Commit();
}
~GeetFS() {
for (int i = 0; i < cmts.size(); i++)
delete cmts[i];
delete uncommited;
}
void solve(const string& cmd) {
if (cmd == "write") {
int offset, len;
string filename, input;
cin >> filename >> offset >> len;
getline(cin, input);
getline(cin, input);
write(filename, offset, len, input);
}
else if (cmd == "read") {
int offset, len;
string filename, output;
cin >> filename >> offset >> len;
RC rc = uncommited->read(filename, offset, len, output);
if (rc == NOEXIT) {
if (head != nullptr) rc = head->read(filename, offset, len, output);
}
if (rc != OK) cout << string(len, '.') << endl;
else cout << output << endl;
}
else if (cmd == "unlink") {
string filename;
cin >> filename;
unlink(filename);
}
else if (cmd == "ls") {
ls();
}
else if (cmd == "commit") {
string cmtname;
cin >> cmtname;
commit(cmtname);
}
else if (cmd == "checkout") {
string cmtname;
cin >> cmtname;
checkout(cmtname);
}
else if (cmd == "merge") {
string me, cmtname;
cin >> me >> cmtname;
merge(me, cmtname);
}
}
void write(string filename, int offset, int len, const string& input) {
File* file = nullptr;
RC rc = uncommited->query(filename, file);
if (rc == DELETED) file->setRecover();
if (rc == NOEXIT) {
if (head != nullptr) rc = head->query(filename, file);
if (rc == OK) uncommited->Copy(*file);
}
uncommited->write(filename, offset, len, input);
}
void unlink(const string& filename) {
File* file = nullptr;
RC rc = uncommited->query(filename, file);
if (rc != OK && head != nullptr) {
rc = head->query(filename, file);
if (rc == OK) uncommited->Copy(*file);
}
uncommited->unlink(filename);
}
void ls() {
map<string, int> ID;
vector<File> tmpFile;
uncommited->ls(tmpFile, ID);
if (head != nullptr) {
head->ls(tmpFile, ID);
}
int num = 0;
string minStr = "", maxStr = "";
for (int i = 0; i < tmpFile.size(); i++) {
if(tmpFile[i].getDel()) continue;
num++;
if (minStr == "") minStr = tmpFile[i].getName();
else minStr = min(minStr, tmpFile[i].getName());
if (maxStr == "") maxStr = tmpFile[i].getName();
else maxStr = max(maxStr, tmpFile[i].getName());
}
cout << num;
if (num > 0)
cout << " " << minStr << " " << maxStr;
cout << endl;
}
Commit* query(const string& cmtname) {
for (int i = 0; i < cmts.size(); i++) {
if(cmts[i]->getName() == cmtname)
return cmts[i];
}
return nullptr;
}
void commit(const string& cmtname) {
if (uncommited->isEmpty() || nullptr != query(cmtname)) return;
uncommited->setName(cmtname);
uncommited->setParent(head);
uncommited->setTime(++Time);
cmts.push_back(uncommited);
head = uncommited;
uncommited = new Commit();
}
void checkout(const string& cmtname) {
Commit* cmt = query(cmtname);
if (!uncommited->isEmpty() || cmt == nullptr) return;
head = cmt;
}
void merge(const string& me, const string& cmtname) {
Commit* meCmt = query(me);
if (!uncommited->isEmpty() || meCmt == nullptr || meCmt == head) return;
uncommited->setName(cmtname);
uncommited->setParent(head, meCmt);
cmts.push_back(uncommited);
head = uncommited;
uncommited = new Commit();
}
private:
Commit* head;
Commit* uncommited;
vector<Commit*> cmts;
};
int main(void) {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
GeetFS gt;
int n;
cin >> n;
while (n--) {
string cmd;
cin >> cmd;
gt.solve(cmd);
}
return 0;
}