题意:
思路:
这道题的解决应该分为建树 + 查找两部分。
关于建树,为了方便后代选择器的查找,采用儿子记录父节点的方法,即每个节点记录自己的父节点位置。采用数组描述这棵树,每行的信息都在数组对应的位置上,关于每行信息,采用结构体描述
struct Element
{
string label;
string id;
int father; //代表父节点位置
int ceng; //小数点的个数除2
}
label是标签信息,id如果没有就是“”,father是他父节点在数组中的位置,ceng就是他的层次。在将n个信息输入进来的时候,通过“.”的个数可以得知在哪一层,根据他的信息可以获得label和id,关于父节点的设置我们可以采用一个单调栈结构来得出,维护这个单调栈从栈顶到栈底层次递减,那么将我们有新的一行信息输入时,我们得到了这一行的层次,接着我们看栈,首先弹出所有层次不比他小的,然后这一行的父节点就是栈顶元素,即最靠近他的比他层次靠前的,若是全弹出去了,那这一行的父节点就是-1。
关于查找,首先判断是三种选择器的哪一个,对于标签和id,直接遍历数组,看有没有合适的即可,对于后代选择器,采用逆序判断的方法,即从后代选择器中的最后一个向前判断,需要设立一个函数,我们依旧是遍历所有的元素,都首先判断遍历到的这个位置满不满足后代选择器中的最后一个,如果不满足就跳过,如果满足就查看这个位置的父节点满不满足后代选择器的倒数第二个,如果不满足就看这个新的位置的父节点满不满足倒数第二个,因为这道题是只有有祖先满足即可,所以没有必要所有的都连着,上一级选择器可能无法满足父亲但是可以满足祖先,那么也算可以匹配!!!
代码部分注意点:
判断字符串中有没有某个元素可以采用find函数。如果有会返回位置,没有的话
(st[i].find("#") == string::npos
可以用string的substr函数将字符串的某部分复制出来
将字符串按照空格划分成新的字符串可以将字符串化为流,然后输入
istringstream s(str);
string st[105];
int tot = 0;
while (s >> st[tot])
{
tot++;
}
代码:
#include<iostream>
#include<map>
#include<string>
#include<stack>
#include<sstream>
using namespace std;
//标签大小写不敏感
bool labelEqual(string& x, string& y)
{
if (x.size() != y.size())return false;
int len = x.size();
for (int i = 0; i < len; i++)
{
if(!(x[i] == y[i] || x[i] == (y[i] + 'A' - 'a') || x[i] == (y[i] + 'a' - 'A')))return false;
}
return true;
}
struct Element
{
string label;
string id;
int father; //代表父节点位置
int ceng; //小数点的个数除2
}element[105];
bool decSuccess(string st[], int cnt, int now)
{
int temp = now;
for (int i = cnt - 1; i >= 0; i--)
{
if (st[i].find("#") == string::npos)//没有"#",标签选择器
{
if (!labelEqual(st[i], element[now].label))
{
if (now == temp)return false;
else i++;
}
}
else
{
string id = st[i].substr(1);
if (id != element[now].id)
{
if (now == temp)return false;
else i++;
}
}
if (i == 0)return true;
if (element[now].father == -1)return false;
else now = element[now].father;
}
}
int main()
{
int n, m;
cin >> n >> m;
stack<int>sta; //用来辅助求取父节点
getchar();
for (int i = 0; i < n; i++)
{
string str;
getline(cin, str);
int k = 0;
for (k; k < str.size(); k++)
{
if (str[k] != '.')break;
}
//k的值为小数点的个数
element[i].ceng = k / 2;
while (!sta.empty() && element[sta.top()].ceng >= k / 2)
sta.pop();
if (sta.empty())element[i].father = -1;
else element[i].father = sta.top();
sta.push(i);
if (str.find("#") == string::npos)//代表没有id
{
element[i].label = str.substr(k);
element[i].id = "";
}
else
{
element[i].id = str.substr(str.find("#") + 1);
element[i].label = str.substr(k, str.find("#") - k - 1);
}
}
for (int i = 0; i < m; i++)
{
string str;
getline(cin, str);
int num = 0; //满足的行的个数
int ans[105]; //分别是哪些行
//标签选择器
if (str.find("#") == string::npos && str.find(" ") == string::npos) //没有空格和“#”
{
for (int k = 0; k < n; k++)
{
if (labelEqual(str, element[k].label))
{
ans[num] = k + 1;
num++;
}
}
}
else
{
//id选择器
if (str.find(" ") == string::npos)
{
string id = str.substr(1);
for (int k = 0; k < n; k++)
{
if (element[k].id == id)
{
ans[num] = k + 1;
num++;
}
}
}
else //后代选择器
{
istringstream s(str);
string st[105];
int tot = 0;
while (s >> st[tot])
{
tot++;
}
for (int k = 0; k < n; k++)
{
if (decSuccess(st, tot, k))
{
ans[num] = k + 1;
num++;
}
}
}
}
cout << num << " ";
for (int k = 0; k < num; k++)
cout << ans[k] << " ";
cout << endl;
}
}