CSP 201809-3 元素选择器
1. 问题描述
2. 思路历程
- 这题也是一道没有用到什么算法的模拟题(可惜我还是觉得挺困难)。先确定模拟的步骤:先读入结构化文档,再为每一个选择器进行选择,最后为每一个选择器输出。
- 对于读入结构化文档,采取按行读入的方法,getline函数一次读入一整行,再用split()函数对这个字符串进行分割。
- 对于为每一个选择器进行选择,由于标签对大小写不敏感,且双方只有用同一种写法时才能比较,因此用tolower函数将所有字母变为小写,便于后面的比较。对于满足条件的选择器,直接加入vector ans。
- 对于为每个选择器输出,直接输出ans的长度和其中的元素即可。
3. 具体实现
- 在split()函数中,先遍历整个字符串找到第一个非 . 元素位置(即标签出现的开头位置)、空格的位置(即属性出现的开头位置),标志量flag为2,表示有属性,否则没有属性。
这里用到了substr()函数获取子字符串,是一个很好用的函数 - 对于判断三种选择器的条件,标签选择器和id选择器比较简单,直接查询比较即可,但是后代选择器需要另外的函数isMatch来另外判断。在isMatch中主要考虑逐级往上查找选择器的祖先。
- 千万注意,在每次处理完一个选择器之后都要清空数组(我在这里错了好久都找不到原因
4. 代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <string.h>
#include <sstream>
using namespace std;
const int maxn = 111;
struct Element
{
string label, id;
int level;
} elements[maxn];
int m, n;
vector<string> qus; // 保存查询
vector<int> ans; // 保存答案
void toLower(string &str)
{
for (int i = 0; i < str.size(); i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
str[i] = tolower(str[i]);
}
}
void split(string &str, int x)
{
int flag = 0, s1 = 0, s2 = 0, s = 0;
string label, id;
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '.') s++;
if (str[i] != '.' && flag == 0) // 第一个非 . 的位置
{
flag = 1;
s1 = i;
}
if (str[i] == ' ' && flag == 1) // 标记属性出现的位置
{
s2 = i + 1;
flag = 2;
break;
}
}
if (flag != 2) // 没有属性
{
label = str.substr(s1);
toLower(label);
elements[x].label = label;
elements[x].level = s / 2;
}
else // 有属性
{
label = str.substr(s1, s2 - s1 - 1);
toLower(label);
id = str.substr(s2);
elements[x].label = label;
elements[x].id = id;
elements[x].level = s / 2;
}
}
// 通过检查元素的祖先判断元素与选择器是否匹配
bool isMatch(int x)
{
int t = elements[x].level;
long sz = qus.size() - 2;
for (int i = x - 1; i >= 0; i--)
{
if (elements[i].level == t - 1)
{
if (qus[sz][0] != '#' && qus[sz] == elements[i].label) sz--;
else if (qus[sz][0] == '#' && qus[sz] == elements[i].id) sz--;
t = elements[i].level;
if (sz < 0) return true;
}
}
return false;
}
int main()
{
cin >> n >> m;
getchar();
ans.clear();
qus.clear();
string s;
for (int i = 0; i < n; i++)
{
getline(cin, s);
split(s, i);
}
for (int i = 0; i < m; i++)
{
getline(cin, s);
stringstream ss;
ss << s;
string st;
while (ss >> st)
{
if (st[0] != '#') toLower(st); // 不是id选择器,可以全部转化成小写
qus.push_back(st);
}
long sz = qus.size();
for (int j = 0; j < n; j++)
{
if (qus[sz - 1][0] == '#' && qus[sz - 1] == elements[j].id || qus[sz - 1][0] != '#' && qus[sz - 1] == elements[j].label)
{
if (sz == 1 || isMatch(j)) ans.push_back(j + 1);
}
}
cout << ans.size();
for (int i = 0; i < ans.size(); i++) cout << " " << ans[i];
cout << endl;
ans.clear();
qus.clear();
}
return 0;
}