Sample input
11 5
html
..head
....title
..body
....h1
....p #subtitle
....div #main
......h2
......p #one
......div
........p #two
p
#subtitle
h3
div p
div div p
Sample output
3 6 9 11
1 6
0
2 9 11
1 11
解题思路
数据类型
首先是节点的结构体,很简单:
struct node{
node(){
key.clear(); id.clear(); parent=0;
}
string key;//标签选择器
string id;//id选择器
int parent;//父亲
};
node a[maxn];
这个结构体中只存储了当前行的标签和id,还有一个父亲节点。a[i]表示行号为 i i i的节点,因此结构体中不需要存储行号。
然后很重要的一个map
map<string,vector<int>> mp;
这个map存储了一个string对应的行号是多少,由于标签可能有多个相同的,所以用一个string对应一个vector。当然,这个map中也存储了id,不过vector的size=1。
还有这个数组:
int level[maxn];
这个数组存储了上一个第 i i i层的节点的行号,什么意思呢,看下面这个示例:
html
..head
....title
..body
....h1
搜到html的时候,定为0级,没有父亲节点,同时level[0]=1;搜到head,这是第1级,父亲节点就是level[1-1]=1,同时level[1]=2;搜到title,这是第2级,父亲节点就是level[2-1]=2,同时level[2]=3;然后搜索到body,这是第1级,父亲节点是level[1-1]=1,同时level[1]=4;搜索到h1,这是第2级,父亲是level[2-1]=4,同时level[2]=5;依次类推,我们可以确定父子关系。
搜索过程
搜索过程十分简单,如果我们搜索div div p,那么首先通过map找到所有p对应的行号,也就是节点的序号,然后遍历这些序号,通过这个节点,搜索向上父亲节点,直到搜索到根节点或者三个标签都找到了,那么这个序号就是正确的,存起来,遍历下一个序号即可。
搜索标签选择器和id选择器的方法与搜索后代选择器完全相同。
PS:不要忘了标签大小写不敏感,id大小写敏感
完整代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <sstream>
using namespace std;
const int maxn=105;
struct node{
node(){
key.clear(); id.clear(); parent=0;
}
string key;//标签选择器
string id;//id选择器
int parent;//父亲
};
node a[maxn];
int n,m,cnt;
map<string,vector<int>> mp;//key,id对应的节点号们
int level[maxn];//上一个第i层的节点号
void SplitAndBuild(string _s){//分割并且构建树
int _level=0,_temp; string _key,_id; _key.clear(); _id.clear();
for (int i=0; i<_s.size(); i++){
_temp=0;
while(_s[i]=='.'){//寻找等级
_temp+=1; i+=2; _level=_temp;
}
if(_temp){
i--; continue;
}
if(_s[i]==' '){
_key=_id; _id.clear(); continue;
}
_id+=_s[i];
}
if(_key.empty()){
_key=_id; _id.clear();
}
for (int i=0; i<_key.size(); i++){//key大小写不敏感,全都转化为小写
if(_key[i]>='A' && _key[i]<='Z') _key[i]+=32;
}
a[++cnt].key=_key; a[cnt].id=_id; level[_level]=cnt;//创建新的节点
mp[_key].push_back(cnt); if(!_id.empty()) mp[_id].push_back(cnt);//加入map
if(_level) a[cnt].parent=level[_level-1];//构建父子关系
}
bool backtrack(int nodeindex,vector<string> _v,int index){
if(index==-1) return true;//提前搜索结束
if(nodeindex==0) return index==-1;//搜索到根节点结束
if(a[nodeindex].id==_v[index] || a[nodeindex].key==_v[index])
return backtrack(a[nodeindex].parent,_v,index-1);
else return backtrack(a[nodeindex].parent,_v,index);
}
void find(string _s){
vector<string> v; v.clear();
string stemp; stemp.clear();
stringstream ss(_s);
while(getline(ss,stemp,' ')){//分割,这个知识点可以看我的博客:https://blog.csdn.net/weixin_43347376/article/details/105134445
if(!stemp.empty()) {
if(stemp[0]!='#'){
for (int i=0; i<stemp.size(); i++){//标签大小写不敏感
if(stemp[i]>='A' && stemp[i]<='Z') stemp[i]+=32;
}
}
v.push_back(stemp);
}
}
vector<int> line; line.clear();
for (int i=0; i<mp[v[v.size()-1]].size(); i++){//从下向上搜索
if(backtrack(mp[v[v.size()-1]][i],v,v.size()-1))
line.push_back(mp[v[v.size()-1]][i]);
}
printf("%lu",line.size());
for (auto it:line) printf(" %d",it);
putchar('\n');
}
int main(){
scanf("%d %d",&n,&m); getchar();
for (int i=1; i<=n; i++){
string s; getline(cin,s);
SplitAndBuild(s);
}
for (int i=1; i<=m; i++){
string s; getline(cin,s);
find(s);
}
return 0;
}