CCF 201809-3 2018年9月第三题元素选择器(C++ 100分题解)

CCF 201809-3 2018年9月第三题元素选择器(C++ 100分题解)

试题编号: 3
试题名称: 元素选择器
时间限制: 1.0s
内存限制: 512.0MB
CCF 201809-3 2018年9月第三题元素选择器(C++ 100分题解)_第1张图片
提交后100分代码:
这道题的大致思路:首先用stringstream对输入的字符串进行分词,然后进行一系列处理(可以直接用if逻辑硬刚,也可以用正则表达式)获得需要的属性,包括标签名、id属性名、节点所在层级等。然后建树,将节点全部插入树中。最后对询问进行反馈,关键是对分词处理后的询问的字符串序列和每个节点(可以用层序遍历、先序遍历、后序遍历)的从根节点到它的路径进行比较。

这道题本身很简单,但是我刚做的时候很多东西都不会,正好借这个题目学习了很多。
第一,对于字符串的处理,要用到stringstream进行分词,可以用正则表达式进行匹配;
第二,数据结构,这其实就是一个简单的普通的多叉树,用层序遍历(随便哪种遍历都行)按照题目要求查找指定的节点即可。
题目里面有些要注意的地方,比如标签选择器大小写不敏感,匹配时都转成小写。


```cpp
#include  
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
string nameOfParent[100];//用来存放每一个层级的最后一个加入的节点的标签名 name。 

struct node{//对树节点的定义  
	int lineNum, level, parentLevel; //lineNum是节点输入的行数,其实就是节点的序号;level是根据每行输入的前部的"."的数量进行等级
									 //的评定,决定该节点在第几层;parentLevel是该节点的父节点的level值。 
	string name, idName, parentName; // name是每行输入的标签,idName是每行输入的id属性,parentName是父亲节点的name值。 
	vector child; //child是每个节点的所有子节点的地址组成的一个数组。  
	vector path; //path是由 从这棵树的根节点一直到该节点这一途径中 所有节点 的地址 组成的一个数组。 
	node* parent; //该节点的父节点的地址。 
};

node* createTreeNode(int lineNum, int level, int parentLevel, string name, string idName, string parentName, node* parent) {//创建树节点 
	node* Node = new node;
	Node->lineNum = lineNum;
	Node->level = level;
	Node->parentLevel = parentLevel;
	Node->name = name;
	Node->idName = idName;
	Node->parentName = parentName;
	Node->child.clear();
	Node->path.clear();
	Node->parent = parent;
	if(parent != NULL) { //如果父节点不为空,将父节点的path数组复制到该节点的path数组。 
		for(int i=0; iparent->path.size(); ++i)
			Node->path.push_back(Node->parent->path[i]);
	}
	Node->path.push_back(Node); //将该节点本身加入到该节点的path数组的末尾。 
	return Node;
}

void insertTreeNode(node* &root, int lineNum, int level, int parentLevel, string name, string idName, string parentName) {//插入树节点 
	if(root == NULL) { //在只有这一棵树的题目中,这个if语句就是针对根节点 
		root = createTreeNode(lineNum, level, parentLevel, name, idName, parentName, NULL);
		return ;
	}
	//如果该节点不是根节点,而是一般的节点,就先遍历他的所有子节点 
	for(int i=0; ichild.size(); ++i) { //!!!我把这个for循环调整到了下面这个if语句上面,因为下面这个if语句判断完之后会加入新的孩子节点,再进行for循环,遇到该节点和父亲节点同名时会导致一直重复这两个步骤 
		insertTreeNode(root->child[i], lineNum, level, parentLevel, name, idName, parentName);
	} 
	if(root->name == parentName && root->level == parentLevel) {//!!!debug发现的错误,因为同一层不会有同名的,所以用层级和名字同时判断来进行锁定,否则可能会出现该节点的名称和父亲节点名称相同的情况 
		root->child.push_back(createTreeNode(lineNum, level, parentLevel, name, idName, parentName, root));
	}
}

bool match(node* root, stack strStack) {//对于每一个节点,判断他的path数组的序列是否和strStack中存放的序列(即输入的询问请求)相符。 
	int len = root->path.size();
	for(int i=len-1; i>=0; --i) {
		node* temp = root->path[i];
		if(strStack.empty()) //如果strStack序列能按照顺序完全和path数组的序列相符,那么strStack的所有元素一定会被逐次全部弹出,strStack最后会成为空栈。 
			return true;
		string str = strStack.top();
		if(str[0] == '#') {//是id属性 
			if(temp->idName == str.substr(1, str.size()-1)) 
				strStack.pop();
			else {
				if(i == len-1) //!!!我是从序列的后端开始比对两个序列的元素是否相符,所以如果从后数第一个元素就不符合就要直接退出。 
					return false;
			} 
		}
		else{// 是标签 
			if(temp->name == str)
				strStack.pop();
			else {
				if(i == len-1)
					return false;
			} 				
		}
	}
	if(strStack.empty())
		return true;
	else //如果此时strStack不是空栈,说明strStack的序列(即输入的询问请求)与该节点的path数组的序列不按照顺序相符。 
		return false;
}

void search(node* root, stack strStack, set &output) { //层序遍历,对每个节点是否符合 strStack的序列(即输入的询问请求)进行判断 
	queue q;
	q.push(root);
	while(!q.empty()) {
		node* currentNode = q.front();
		if(match(currentNode, strStack))
			output.insert(currentNode->lineNum);
		q.pop();
		for(int i=0; ichild.size(); ++i) {
			if(currentNode->child[i] != NULL) 
				q.push(currentNode->child[i]);
		}
	}
}

int main () {
	int n, m;
	cin >> n >> m;
	string tab;
	getline(cin, tab);
	node* root = NULL; //初始化树 
	
	for(int i=1; i<=n; ++i) { //第一部分:数据处理,通过stringstream进行分词,通过正则表达式对需要的数据进行提取 
		string str, temp, name, idName = "noIdName", parentName = "noParentName";
		int level, parentLevel = -1;
		getline(cin, str);
		stringstream ss;
		ss << str;
		queue q;
		while(ss >> temp) {
			if(temp[0] == '.') {//是标签 
				regex re("[.]*");
				smatch result;
				if(regex_search(temp, result, re)) {
					level = result.str().length() / 2; 
					regex re2("[a-zA-Z0-9]{1,}");
					smatch result2;
					if(regex_search(temp, result2, re2)) 
						name = result2.str();
					for(int j=0; j output;  //第二部分:对输入的查询请求进行反馈 
	stack strStack;
	for(int i=0; i> temp) {
			if(temp[0] != '#') {
				for(int j=0; j::iterator it=output.begin(); it!=output.end(); ++it) {
			cout << " " << *it;
		}
		cout << endl;
	}
	return 0;
}

你可能感兴趣的:(ccf,csp认证,数据结构,c++,树结构,csp,正则表达式)