201809-3 元素选择器
时间限制:1.0s
内存限制:256MB
题目描述:
分析
依据题意,我们需要对选择器进行匹配,找出全部满足的位置:
- 单独lable的匹配
- 单独id的匹配
- 嵌套lable或id的匹配
其中1、2两个匹配比较好容易,遍历一遍结构化文档(即输入的n行)即可,对于第3个匹配,需要满足以下几点:
-
祖先元素必须在匹配元素之前,即下面示例只有第一个div是p祖先元素
div ..p div
-
祖先元素必须和匹配元素在同一个树结构内,即下面示例只有第二个div是p祖先元素
div ..#id div ....p
-
祖先元素的缩进必须小于后一个祖先元素or匹配元素
解法
-
选择合适的结构存储结构化文档(n行输入)
经过分析我们可以知道,输入的每一行有以下几种可能,并且lable和id不会重名
- 单个lable
- 单个id
- lable id
因此选择以下数据结构存储输入:
struct Node { string lable, id; int cnt; }a[N];
-
读入结构化文档
需要注意的是lable不区分大小写,一般这种情况我们将其转换为小写存储便于操作。
for(int i = 0; i < n; i++) { int cnt = 0, flag = 0; string lable = "", id = ""; while((c = getchar()) != '\n') { if(c == '.') { cnt++; continue; } if(c == ' ') { flag = 1; continue; } if(flag == 1) { if(c != '\n') id += c; else flag = 0; } else lable += c; } //lable变小写存储 transform(lable.begin(),lable.end(),lable.begin(),::tolower); a[i].cnt = cnt; a[i].id = id; a[i].lable = lable; }
-
读入选择器
同样我们需要将lable的选择器转换成小写存储。选择器有可能是
' '
分割的多级,因此用vector<string>
存储。//读入一个选择器 string s = "";//注意初始化为空便于+=操作 vector<string> v; vector<int> ans; while((c = getchar()) != '\n') { if(c == ' ') { if(s[0] != '#') { transform(s.begin(),s.end(),s.begin(),::tolower); } v.push_back(s); s = ""; } else { s += c; } } if(s[0] != '#') { transform(s.begin(),s.end(),s.begin(),::tolower); } v.push_back(s);
-
选择元素
首先说明外面的大循环,对于n行中每行,都判断是否能被选择器中最后一项选择,如果能并且祖先也满足(selector返回true),则成功选择,将其加入结果向量。
注意由于lable和id不重名,故在判断是否存在于n行中的时候无需区分,直接同时和lable和id对比即可。int len = v.size(); //对每个存在于文档中的末级元素,若所有祖先满足则成功select for(int j = 0; j < n; j++) { if(a[j].id == v[len-1] || a[j].lable == v[len-1]) { int end = j, cnt = a[j].cnt, k; for(k = len-2; k >= 0; k--)//判断祖先是否满足 { if(!selector(0,end,v[k],cnt))break; } if(k < 0) ans.push_back(j+1); } }
祖先的判断函数:
bool selector(int start, int& end, string s, int& cnt) { for(int i = end-1; i >= 0; i--) { if(a[i].cnt < cnt) { cnt = a[i].cnt;//只有在同一棵树内才符合祖先定义 end = i; if(a[i].lable == s || a[i].id == s)return true; } } return false; }