题目
思路
- 原思路是建立一棵树,然后如果要进行匹配,就从根节点开始查找,但是可能复杂度比较高,因为这个的多级的后代选择器在匹配时,会比较麻烦。
- 后来参考了一下网上是思路,发现更简单、快速。
- 先进行n行输入,把每一行都输入进node类型的数组nd,包括lable、id、nb等信息,lable是标签,nb是记录的此行共有几个点,也就是级别。
- 如果搜索的时候,只给出了一级,那么只要遍历数组nd,寻找有没有匹配的lable或者id即可。
- 若是多级别搜索,那么只要判断最低级别的p,先找到它,然后对它高的等级的id/标签的map数组an相应的值+1。最后再判断,看所有比p高等级的id/标签的an的值是否足够大(也就是看遍历的次数是否足够)。
注意点
- 这里使用了字符串分割函数strtok();
1、函数的作用是分解字符串,所谓分解,即没有生成新串,只是在s所指向的内容首次出现分界符的位置,将分界符修改成了’/0’,故第一次用strtok()返回第一个子串
2、第一次提取子串完毕之后,继续对源字符串s进行提取,应在其后(第二次,第三次。。。第n次)的调用中将strtok的第一个参数赋为空值NULL(表示函数继续从上 一次调用隐式保存的位置,继续分解字符串;对于前一次次调用来说,第一次调用结束前用一个this指针指向了分界符的下一位)
3、当this指针指向“\0” 时,即没有被分割的子串了,此时则返回NULL
4、可以把delim理解为分隔符的集合,delim中的字符均可以作为分隔符。
5、strtok在调用的时候,如果起始位置即为分隔符,则忽略了起始位置开始的分隔符
demand.clear();
char *p=strtok(s," ");
while(p){
demand.push_back(p);
p=strtok(NULL," ");
}
这里是对s进行分割,以“ ”为分隔符。第一次使用是第一个变量是s(待分割的字符串),后面的第一个变量都是“NULL”。
代码
#include<iostream>
#include<map>
#include<cstring>
#include<vector>
#include<ctype.h>
#include<string>
#include <string.h>
using namespace std;
struct node{
string lable,id;//标签和id
int nb;//前面点的个数
}nd[110];
//大写转换成小写
string change(string s){
for(int i=0;i<s.size() ;i++){
if(s[i]>='A'&&s[i]<='Z'){
s[i]=s[i]+'a'-'A';
}
}
return s;
}
map<string,int>an,query;//an表示各个祖先含有的标签,id属性等,query表示要查询的
vector<string>demand;
//以空格进行分割字符串s,以空格为间隔的字符串都入demand数组中
void split(char* s){
demand.clear();
char *p=strtok(s," ");
while(p){
demand.push_back(p);
p=strtok(NULL," ");
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);//n为结构化文档的行数,m为查询次数
getchar();//为getline读取回车
for(int i=1;i<=n;i++){//此部分为将数据存到数组nd里面
string str;getline(cin,str);
//先找出....的个数
int cnt=0;
for(int j=0;j<str.size();j++) {
if(str[j]=='.')cnt++;//....的个数
else break;
}
nd[i].nb=cnt; //0-(cnt-1)是...的位置
//接下来是标记部分,
int len=0;
int pos=-1;//记录了id从pos开始,如果有id的话
for(int j=cnt;j<str.size() ;j++,len++){
if(str[j]==' '){
if(j+1<str.size()&&str[j+1]=='#')
pos=j+1;
break;
}
}
//label记录标签,是s的cnt到len
nd[i].lable=str.substr(cnt,len);
nd[i].lable=change(nd[i].lable); //由于标签的匹配大小写不敏感,因此化为小写
if(pos==-1)//如果没有id属性,就让id=""
nd[i].id="";//id
else
nd[i].id=str.substr(pos);//pos及以后都是id
}
//查询操作
while(m--){
//getchar();//为getline读取回车
char d[100];
gets(d);
split(d);//以空格进行分割字符串s,以空格为间隔的字符串都入demand数组中
vector<int>ans;
if(demand.size() ==1){//demand中只有一条指令
string ss=demand[0];
if(ss[0]!='#')//查询标签大小写不敏感
ss=change(ss);//化为小写
//进行匹配
for(int i=1;i<=n;i++)
if(ss==nd[i].lable||ss==nd[i].id)
ans.push_back(i); //保存行号
}
else{
query.clear();
for(int i=0;i<demand.size()-1 ;i++){
if(demand[i][0]!='#'){//标签转小写
demand[i]=change(demand[i]);
}
query[demand[i]]++;//用query来记录标签/id
}
string q=demand.back() ;
if(q[0]!='#')//查询标签大小写不敏感
q=change(q);//化为小写
for(int i=1;i<=n;i++){//遍历整个nd数组
if(q==nd[i].lable||q==nd[i].id){//最后面的q可以匹配
an.clear();
for(int j=i-1;j>0&&nd[j].nb<=nd[i].nb;j--){//从第i-1行往上遍历,直到第j行的级别比i要高
if(nd[j].nb==nd[i].nb)continue;//关键:细节
an[nd[j].lable]++;//由于标签不会为空,因此直接加一
if(nd[j].id!="")//只将不为空的id进行加一
an[nd[j].id]++;
}
bool flag=true;
//遍历查询的所有id选择器和标签选择器 ,判断i是否合适
for(map<string,int>::iterator it=query.begin();it!=query.end();it++)
if(an.count(it->first)==0||an[it->first]<it->second){//如果i的祖先没有或者有但是个数不够
flag=false;
break;
}
if(flag)
ans.push_back(i);
}
}
}
printf("%d",ans.size());
for(int i=0;i<ans.size();i++)
printf(" %d",ans[i]);
printf("\n");
}
return 0;
}