题目的主要信息:
- 开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号,及相同错误出现的次数
- 记录最多8条错误记录,循环记录,最后只用输出最后出现的八条错误记录
- 对相同的错误记录只记录一条,但是错误计数增加,最后一个斜杠后面的带后缀名的部分(保留最后16位)和行号完全匹配的记录才做算是”相同“的错误记录
- 输入的文件可能带路径,记录文件名称不能带路径,超过16个字符的文件名称,只记录文件的最后有效16个字符
- 循环记录时,只以第一次出现的顺序为准,后面重复的不会更新它的出现时间,仍以第一次为准。如果个超过8条的错误中第一条出现了它,最后一条也是它,不应该输出,以它是第一条出现的为准
方法一:暴力法
具体做法:
我们可以在vector数组中嵌套两层pair,由此可以记录文件名、行号、出现次数,记为record。同时,我们准备一个8个长度的vector的pair数组用指针循环记录将要输出的文件和行号,记为res。
对于每一行输入,首先要做的提取文件名:我们从字符串末尾开始遍历,找到第一个反斜杠,将这些字符组装成文件名,如果文件名大于16,我们要用substr函数题取最后16位。
题取文件名以后,我们检查这个文件及这个行号是否在record数组中出现过了,这里遍历record数组依次检查即可,如果出现过就将其记录的出现次数加1,其他不做变动;如果没有出现过,则将其添加到record数组的末尾,然后将其添加进res数组中,这里要循环使用空间,即下标,这也刚好符合第一次出现的位置为准。
最后我们一共循环8次,每次要输出res数组中记录的字符串非空的文件名(防止不足8次的错误)、行号即record中与之对应的出现次数。
#include<iostream>
#include<vector>
using namespace std;
string getfilename(string filepath){ //题取文件名
string res = "";
for(int i = filepath.length() - 1; i >= 0; i--){ //逆向查找到第一个斜杠
if(filepath[i] == '\\')
break;
res = filepath[i] + res; //将字符加到字符串前面
}
if(res.length() > 16) //长度大于16的时候,截取后16位
res = res.substr(res.length() - 16, 16);
return res;
}
bool find(vector<pair<pair<string, int>, int>>& record, string& file, int num){ //找到出现过的文件名和行号
for(int i = 0; i < record.size(); i++){
if(record[i].first.first == file && record[i].first.second == num){ //文件名和行号相同
record[i].second++; //直接在后面添加出现次数
return true;
}
}
return false;
}
void findoutput(vector<pair<pair<string, int>, int>>& record, string& file, int num){ //打印该错误出现的次数
for(int i = 0; i < record.size(); i++){
if(record[i].first.first == file && record[i].first.second == num){ //文件名和行号相同
cout << record[i].second << endl; //直接输出次数
return;
}
}
}
int main(){
string filepath;
int num;
vector<pair<pair<string, int>, int> > record; //记录文件名、行号、出现次数
vector<pair<string, int> > res(8, {"", 0});
int index = 0; //记录下标
while(cin >> filepath >> num){
string file = getfilename(filepath); //提取文件名
if(!find(record, file, num)){ //出现新的才添加
record.push_back(make_pair(make_pair(file, num), 1)); //记录中添加一个全新的
res[index] = make_pair(file, num);
index = (index + 1) % 8; //循环
}
}
for(int i = 0; i < 8; i++){
if(res[index].first != ""){ //只输出有记录的,防止不足8个
cout << res[index].first << " " << res[index].second << " ";
findoutput(record, res[index].first, res[index].second);
}
index = (index + 1) % 8;
}
return 0;
}
复杂度分析:
- 时间复杂度:,一共个字符串,每个字符串的查找都是
- 空间复杂度:,记录字符串的长度最坏为
方法二:哈希表
具体做法:
我们可以用哈希表来代替上述暴力查找,省去不少时间。同时,方法一中嵌套的pair过多,我们可以将输入的行号也看成是字符串,将文件名+空间+行号,这样组成一个新的字符串看成哈希表的key值,而这个key值出现的次数即为哈希表的value值。这样我们每次只需要检查哈希表中没有出现过这一串key值就可以,添加新的key也要将其纳入res数组中,同时循环储存,最后输出的时候也是查哈希表找到出现次数。
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
string getfilename(string filepath){ //题取文件名
string res = "";
for(int i = filepath.length() - 1; i >= 0; i--){ //逆向查找到第一个斜杠
if(filepath[i] == '\\')
break;
res = filepath[i] + res; //将字符加到字符串前面
}
if(res.length() > 16) //长度大于16的时候,截取后16位
res = res.substr(res.length() - 16, 16);
return res;
}
int main(){
string filepath, num; //把路径和行号都当成字符串
unordered_map<string, int> mp;
vector<string> res(8, "");
int index = 0; //记录下标
while(cin >> filepath >> num){
string file = getfilename(filepath);
string key = file + " " + num;
if(mp.find(key) == mp.end()){ //没有出现过,需要添加到哈希表中
mp[key] = 1;
res[index] = key;
index = (index + 1) % 8; //循环记录
}else
mp[key]++; //遇到相同的错误,计数增加
}
for(int i = 0; i < 8; i++){
if(res[index] != "") //只输出有记录的,防止不足8个
cout << res[index] << " " << mp[res[index]] << endl;
index = (index + 1) % 8;
}
return 0;
}
复杂度分析:
- 时间复杂度:,对于unordered_map每次查找和插入都是,一共操作
- 空间复杂度:,哈希表最大长度为