又是A了一天才A出来的题目,题目很长,可以去官网看,这里梳理一下题目细节。
- 标签大小写不敏感,id敏感
- 标签有层级之分(id也是)
- 测试用例可能会查询第一层标签
- 用例选择器有三种情况:标签选择器,id选择器,两者复合的后代选择器(重点!!!!)
- 题目保证出现的标签和元素不会重复,且标签和元素内部没有空格
前面三点的理解应该不难,大部分80分的人都能处理,问题出就出在复合选择器上,我们看一下题目原文:
这里让人产生歧义的就是“A和B均为标签选择器或id选择器”。这个均为一次读过去就会认为是每个后代选择器中所有的项目都是一样的,但其实多个查询项目时,会有嵌套或者混合选择器出现。这句话的意思应该是“A和B均有标签选择器或id选择器”。
理解这个后,坑就基本没有什么了。
接下来是我的代码思路:
-
看到题目中有层级关系时,一般是建一棵树,我这里直接将id和标签近似处理,一起存放在一个数组里面,这样就不用纠结后代选择器里的嵌套查询,出现一个元素我就查询一次数组就行了。
-
剩下的操作就是解析字符串,得到层级,标签,id内容。这些基本没有问题
-
只有一个元素的选择器,就遍历数组就有结果。多个元素时,我们先从前往后匹配最后一个元素,然后再从匹配位置从后往前匹配其他项目,能全部匹配时就记录匹配到的最后一个元素的的行数。数组遍历完就出答案了。
下面是代码,我基本每行都注释了,便于有需要的人理解。
#include <iostream>
#include <sstream>
#include <cctype>
#include <vector>
using namespace std;
struct node//标签和元素都是用同一个结构体
{
string text;//待查询内容
int lever;//层级
int row;//所在行数
};
node arr[210];//题目行数最大 100行,每行最多一个标签和一个元素,也就200个内容
int cot;//用来记录arr可用下标的
string Lower(string s)//字符串全部转换为小写
{
for(int i = 0; i < s.length(); i++)
s[i] = tolower(s[i]);
return s;
}
void add(string s, int i)//处理输入数据,s为数据,i为行数
{
arr[cot].row = i;//记录当前的行数
int le = 0;//该行的层级初始为0
if(s.find('.') != string::npos)//先计算层级
le = (int)s.find_last_of('.') + 1;
arr[cot].lever = le / 2;//点数除2得到层级,其实不处理也没关系
string name, id;//name为标签,id即为id(如果存在的话)
if(s.find(' ') != string::npos)//如果该行出现空格,即存在id
{
int pos = (int)s.find(' ');
id = s.substr(pos + 1);//取出id内容
name = s.substr(le, pos - le);//取出标签内容
}
else name = s.substr(le);//不存在id直接取出标签内容
arr[cot].text = Lower(name);//记录标签内容
if(!id.empty())//这里不分标签和id,将id像标签那样记录
{
cot++;
arr[cot].row = i;
arr[cot].text = id;
arr[cot].lever = le / 2;
}
cot++;//递增下标
}
void deal(vector<string> &v, vector<int> &ans)//处理查询指令
{
int begin;
if(v.size() == 1)//选择器只有一项
{
for(int i = 0; i < cot; i++)//直接遍历数组,记录所有符合结果的行数
if(v[0] == arr[i].text)
ans.push_back(arr[i].row);
}
else if(v.size() > 1)//选择器多个项目时
{
for(int i = 0; i < cot; i++)//遍历数组,寻找最后一个查询的项目是否出现
if(v.back() == arr[i].text)// 匹配到最后一项
{
int cnt = 0, limit = arr[i].lever;//记录当前匹配的层级,往前寻找的项目层级必须小于limit
begin = i - 1;//存在多个指令时,向前查询的起始下标;
for(int a = v.size() - 2; a >= 0; a--)//从最后一项往前匹配,起始下标就是n - 2;
for(int b = begin; b >= 0; b--)
if(arr[b].text == v[a] && arr[b].lever < limit)// 匹配成功
{
cnt++;//记录匹配数
limit = arr[b].lever;//更新层级,下一个匹配项层级要比limit小
begin = b - 1;//更新回溯时的起始下标,避免重复
break;
}
if(cnt == v.size() - 1) ans.push_back(arr[i].row);//如果选择器所有项匹配成功,记录结果
}
}
}
int main()
{
int n, m; cin >> n >> m; cin.ignore();
string s;
for(int i = 1; i <= n; i++)//输入处理数据
{
getline(cin, s);
add(s, i);
}
for(int i = 0; i < m; i++)
{
getline(cin, s);
vector<string> v;
istringstream ss(s);//使用字符串输入流比较简便
while(ss >> s) v.push_back(s[0] == '#' ? s : Lower(s));//如果是标签,就要转换小写,id不用
vector<int> ans;
deal(v, ans);
cout << ans.size();
for(auto i : ans) cout << " " << i;//输出结果
cout << endl;
}
return 0;
}