题意:
思路:
题目很长,而且给的一开始的引入看的很吓人。然后通读后面的题目可以发现,即给定你一些文件的名字,有n个。然后有的可能有描述性的属性。同时每个这样的文件都会在前面的一个文件下,即为子节点。到了这里,感觉和目录管理器相似了。同时要注意其每个名字的label属性大小写不分,其中只包含有数字和字母。然后如果有描述性属性,则前面是以#开头。后面的m行则为查找的描述性信息。其中查找器分为三类,第一类是一个简单的label属性查找,第二类为简单的id属性查找,第三类则为多查找。
分析完题目开始进行题目的解析。首先是想到建树,但是后来尝试异常麻烦,特别是多查找的时候,需要反复从头遍历到整棵树,看看有没有这样的子节点。然后就用的数组模拟的,其中当然需要结构体。结构体中包含label,id两个属性。以及自己的层次和父节点。其中父节点记录其父节点所在行,即可使用这个来当作索引直接找到父节点。然后构建系统的时候,用了一个结构体的vector来保存所有的信息。每次读取一行,然后读取前面的“ . ”的数量,点的数量除以2即可当作其层次。这里要注意的是第一行html没有父节点,其他的可能也会有前面没有点的。故当作没有父节点。然后开始从vector最后的位置向前遍历,找到第一个层次数小1的即为父节点,记录下来。 接下来后面处理label和id,这里比较好处理,id前面有空格和“#”即可记录为有id。然后将这些信息储存好,放到vector中。然后接下来行需要查找的。如果是单一的label或者是id查找,则只需要从头到尾遍历一遍就可以。如果符合,则存放到result中,最后输出总数量和每一个符合的行号。如果是多查找的时候,则从尾反向遍历,因为子节点总是会在下面。如果找到符合得了,则根据该节点记录得父节点得行号直接跳跃就可以了。这题中自己觉得这个父节点得行号记录下来还是非常重要的。可以加速。查找也可以更方便了。然后讲一下题目中的巨坑的地方。一开始将label和id分开读,即根据题意是大概label完后面会更上一个空格然后#表示描述信息。然后一开始就先将label读完遇到空格之后就去读id。然后卡在60分,然后想到可能中间有空格的部分,于是变成遇到#就去读label还是卡在那。最后将两个放在一块读才过的。难道时有id 和label交叉输入的情况吗。。这也太残暴了。当然也可能是自己处理的问题。然后多级查找到的时候,按照题目的意思应该是要么多级的id要么多级的label,然后怎么都过不去,最后改了下两个混合的才过了。感觉这几次的坑都怪怪的。
代码:
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n,m;
struct html{
int level,father; //父节点指向父亲所在的行
string label,id;
};
vector<html> Html; //存放每行的记录
vector<int> result; //存储要输出的结果
char change(char t){
if(t>=97) t-=32;
return t;
}
int main(){
cin>>n>>m;
getchar(); //处理掉回车
while(n--){
int fa,level; //分别记录父节点和行数
string s,label,id;
getline(cin,s);
int p = 0; //记录点数
while(s[p] == '.') p++;
level = p / 2; //层级数
if(p == 0 || !Html.size()){
fa = -1 ; //没有父节点或者是第一个
}else{
for(int i = Html.size() - 1;i >= 0;i--){
if(Html[i].level == level - 1){ //找到其父节点的层数
fa = i;
break;
}
}
}
int r = false;
while(p < s.size()){ //找出后面的label和id
if(s[p] == ' '){
p++;
continue;
}
if(s[p] == '#'){ //有描述信息id
r = true;
id += s[p];
p++;
continue;
}
if(r) id += s[p]; //描述信息
else label += change(s[p]); //全部转化为小写
p++;
}
Html.push_back({level,fa,label,id});
}
while(m--){ //读取查找的信息
string s;
getline(cin,s);
int p = 0;
while(p != s.size() - 1 && s[p] != ' ') p++;
if(p == s.size() - 1){//单一选择器
if(s[0] == '#'){ //id特征查找
for(int i = 0;i < Html.size();i++){
if(s == Html[i].id) result.push_back(i+1);
}
}else{ //label,不区分大小写
for(int i = 0;i < s.size();i++){
s[i] = change(s[i]);
}
for(int i = 0;i < Html.size();i++){
if(s == Html[i].label) result.push_back(i+1); //找到
}
}
cout<<result.size()<<" ";
for(int i = 0;i < result.size();i++) cout<<result[i]<<" ";
}else{ //多个查找
int j = 0;
vector<string> v;
p = 0;
bool flag = true;
while(p < s.size()){
if(s[p] == '#') flag = false;
if(s[p] == ' '){
flag = true;
v.push_back(s.substr(j,p-j));
j = p + 1;
}
if(flag) s[p] = change(s[p]); //id不在意大小写,故全转换为小写
p++;
}
v.push_back(s.substr(j,p-j));
for(int i = Html.size() - 1;i > 0;i--){ //父节点在上面
int q = v.size() - 1; //要查找的文件的个数
if(v[q] != Html[i].label && v[q] != Html[i].id) continue;
p = Html[i].father; //找到,则直接去找其父节点
q--;
while(q >= 0 && p >= 0){
if(Html[p].label == v[q] || Html[p].id == v[q]) q--;
p = Html[p].father;
}
if(q == -1) result.push_back(i+1); //说明找到
}
cout<<result.size()<<" ";
for(int i = result.size()-1;i >= 0;i--) cout<<result[i]<<" ";
}
cout<<endl;
result.clear();
}
return 0;
}