题目描述
样例
输入
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
输出
3 6 9 11
1 6
0
2 9 11
1 11
思路分析
元素的存储:
创建结构体Node来存储元素,包括标签(label),id属性(id)和级数(rank)。
每一级缩进用两个小数点表示,所以rank=点数/2;
由于标签是大小写不敏感的,所以在存放时都化为小写。
如何判断父元素:
从元素E往前搜索,第一个级数小于E级数元素,即是元素E的父元素。
如何进行选择:
因为选择器可能是复合表达式,所以用vector数组存放一个选择器的内容。当有多个单词时,先遍历所有元素,找到与最后一个单词匹配的一行元素,然后由这一行不断的向上寻找与前一个单词匹配的父元素,直到所有单词匹配完成或当前单词匹配元素失败。将每次匹配的元素行号插入一个vector中保存,成功则输出。
例:div(1) div(2) p
遍历结构体数组中元素
第一次匹配到p,第6行;
从第6行往上搜索,找父元素,第4行body与div(2)不匹配,继续向上找父元素,第1行html不匹配,结束。第二次匹配到p,第9行;
从第9行往上搜索,找父元素,第7行与div(2)匹配;
从第7行往上搜索,找父元素,第4行body与div(1)不匹配,继续向上找父元素,第1行html不匹配,结束。第三次匹配到p,第11行;
从第11行往上搜索,找父元素,第10行与div(2)匹配;
从第10行往上找父元素,第7行与div(1)匹配;
所有单词匹配完成,成功,将行号11插入vector中。第四次……没有第四次,元素遍历完成。
将选择器中几个单词储存进vector可以利用stringstream进行字符串分割,非常方便,不必遍历字符串找空格,再进行分割什么的。
AC代码
#include<iostream>
#include<cstring>
#include<vector>
#include<sstream>
using namespace std;
struct Node
{
string lable,id;//标签 id 注意标签要转化成小写,因为是大小写不敏感的
int rank;//级别 =点数/2
};
Node a[110];
int n,m;
void find()
{
string s,temp;
getline(cin,s);
vector<string> query;//存储查询
vector<int> ans;//存储结果
stringstream ss(s);
while(getline(ss,temp,' '))//字符串分割
{
if(!temp.empty())
{
if(temp[0]!='#') //是标签
{
for(int i=0;i<temp.size();i++)//转化为小写
{
if(temp[i]>='A'&&temp[i]<='Z')
temp[i]+=32;
}
}
query.push_back(temp);
}
}
int len=query.size();
for(int i=1;i<=n;i++) //遍历n行元素
{
if(query[len-1]==a[i].lable||query[len-1]==a[i].id)
{
int start=i,rank=a[i].rank,j=len-2;
for(;j>=0;j--) //向上寻找各级祖先
{
int flag=false;
for(int k=start;k>=1;k--)
{
if(a[k].rank<rank) //第一个级数小于rank的, 是其父节点
{
rank=a[k].rank,start=k; //上移到父节点
if(query[j]==a[k].lable||query[j]==a[k].id) //匹配
{
flag=true;//成功
break;
}
}
}
if(!flag) break; //成功则继续匹配父节点,否则跳出循环
}
if(j<0)//成功
ans.push_back(i);
}
}
cout<<ans.size();
for(int i=0;i<ans.size();i++)
{
cout<<" "<<ans[i];
}
cout<<endl;
}
int main()
{
cin>>n>>m;
getchar();
string str;
for(int i=1;i<=n;i++)
{
getline(cin,str);
int pos1=-1,pos2=-1,num=0;//记录标签 / id位置 和点数
for(int j=0;j<str.size();j++)
{
if(str[j]=='.') num++;
else if(str[j]=='#')
pos2=j;
else if(pos1==-1)
pos1=j;
}
a[i].rank=num/2;
if(pos2!=-1)//有id
{
a[i].lable=str.substr(pos1,pos2-pos1-1);
a[i].id=str.substr(pos2);
}
else
{
a[i].lable=str.substr(pos1);
a[i].id="";
}
//将标签转化为小写
for(int j=0;j<a[i].lable.size();j++)
{
if(a[i].lable[j]>='A'&&a[i].lable[j]<='Z')
a[i].lable[j]+=32;
}
}
for(int i=0;i<m;i++)
{
find();//m次查询
}
return 0;
}