csp真题:201809-3 元素选择器(月模拟)

题目具体地址为:201809-3 元素选择器

时间限制:1.0s
内存限制:256.0MB

问题描述:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分析

  • 标签选择器和id选择器都比较好实现,比较麻烦的是后代选择器的实现。
  • 提示中说:多级的后代选择器在匹配时,可以采用贪心的策略:除最后一级外,前面的部分都可以尽量匹配层级小的元素。因为后代选择器从最后一代开始查找,找到匹配的最后一级后,从这一行向上通过缩进值的判断在其父节点中进行倒数第二级的查找,而由于题目中的贪心策略,在查找父节点时,又是从第一行开始向下查找,直到遍历到后一级所在的那一行。其中,每次一个父节点找到后,标记index–,即需要查找的元素-1,直到index==-1,即从最后一代到最开始一代全部查找到了,就在答案中放入最后一级所在的行数。
  • 其中要多次用到了字符串的操作。如getchar();吃掉回车便于getline(cin, s);用来接收一行数据。gets(tmp);来得到带空格的字符串,而strtok(tmp, " ");用来分割字符串,相应的有对分割部分进行处理的操作:
	char *st = strtok(tmp, " ");//空格分割,按序存放在qu中 
	while (st)
	{
		qu.push_back(st);
		st = strtok(NULL, " ");
	}
  • 还有string中substr(pos2)的使用,是用来取得pos2开始到字符串末尾的子串。同样也有substr(pos1, pos2 - pos1 - 1);用来取得pos1开始的pos2 - pos1 - 1个字符的子串。
  • 同时题目中还要求要大小写不敏感,即可用将所有元素转为小写,用到了s[i] = tolower(s[i]);。

C++

#include<bits/stdc++.h>
using namespace std;

const int N = 105;
int n, m;
string s;
struct Row {
	string lable, id;//标签和属性 
	int blank;//缩进 
}row[N];

//将字符串化成小写 
void toLower(string &s)
{
	for (int i = 0; i < s.length(); i++)
		s[i] = tolower(s[i]);
}

bool search(Row a[], int &line, int &cnt, string s)
{
	for (int i = line; i >= 1; i--)//从这一行开始向上进行遍历查找
	{
		if (a[i].blank < cnt)
		{//缩进值符合条件的,即为其父节点
			cnt = a[i].blank, line = i;//保存该父亲节点所在的行数和缩进值,用于查找成功赋值给结果
			if (s == a[i].lable || s == a[i].id) return true;//找到符合条件的标签或者id则返回
		}
	}
	return false;//查询失败 
}

int main()
{
	cin >> n >> m;//读入n和m 
	getchar();
	for (int i = 1; i <= n; i++)
	{
		getline(cin, s);
		//pos1为标签的起始位置,pos2为id属性的起始位置,cnt为缩进 
		int pos1 = -1, pos2 = -1, cnt = 0;
		for (int j = 0; j < s.length(); j++) {
			if (s[j] == '.')
				cnt++;
			else if (pos1 == -1 && s[j] != '#')
				pos1 = j;
			else if (s[j] == '#')
				pos2 = j;
		}
		row[i].blank = cnt;
		if (pos2 == -1)//如果不存在id属性 
		{
			row[i].lable = s.substr(pos1);
			row[i].id = "";//置为空 
		}
		else//存在id属性 
		{
			row[i].lable = s.substr(pos1, pos2 - pos1 - 1);//从pos1开始将这pos2 - pos1 - 1个字符的子串赋值给lable
			row[i].id = s.substr(pos2);//从pos2开始直到结尾的所有字符子串给id
		}
		toLower(row[i].lable);//统一换成小写 
	}
	for (int i = 0; i < m; i++)//读入m个查询 
	{
		char tmp[100];
		vector<string>qu;//存储查询 
		vector<int>ans;//存储结果 
		gets(tmp);//读入 
		char *st = strtok(tmp, " ");//空格分割,按序存放在qu中 
		while (st)
		{
			qu.push_back(st);
			st = strtok(NULL, " ");
		}
		int len = qu.size();
		for (int j = 0; j < len; j++)//将标签统一化成小写 
			if (qu[j][0] != '#')  toLower(qu[j]);
		for (int j = 1; j <= n; j++)//遍历n行元素 
		{
			if (qu[len - 1] == row[j].id || qu[len - 1] == row[j].lable)//从最后一级子选择器开始依次查找
			{
				int line = j, cnt = row[j].blank, index = len - 2;//index用来记录下标,当其为-1时说明查询中的字符串都处理完毕了
				for (; index >= 0; index--)
				{
					if (!search(row, line, cnt, qu[index])) break;
				}
				if (index < 0)//成功
					ans.push_back(j);
			}
		}
		//输出结果 
		cout << ans.size();
		for (int j = 0; j < ans.size(); j++)
			cout << " " << ans[j];
		cout << endl;
	}
	return 0;
}

当初准备转专业考试时,正好也看了这道题,参考的是java版的,参考代码如下,基本思想差不多。也记录一下。

JAVA

package ccf;
import java.util.Scanner;

public class Main_2018_09_3_1 {

	static class Search {
		int textLines;
		String[] text;
		StringBuilder[] result;
		boolean flag;
		
		public Search(int textLines, String[] text, StringBuilder[] result) {
			this.textLines = textLines;
			this.text = text;
			this.result = result;
			this.flag = true;
		}
		//递归匹配                           从文档何处开始            选择器数组   选择器哪一项   当前匹配项    选择器总长
		public boolean find(int start,String[] info,int num,int i,int len) {
			if(num<len && flag) {//匹配选择器分割后的每一项
				for(int j=start;j<textLines;j++) {
					if(info[num].startsWith("#")) {//是属性id,区分大小写
						if(text[j].contains(info[num])) {//第一项匹配成功
							if(len-1 == num) {//选择器每一项都成功匹配
								result[i].append((char)(j+1+'a'));//文档序号加入序列
								flag = false;
							}
							else //从下一位置开始匹配选择器后序项
								find(j+1,info,num+1,i,len);
						}
					} else {//不是属性id,忽略大小写,全部以小写形式匹配
						if(text[j].toLowerCase().contains(info[num])) {
							if(len-1 == num) {//选择器最后一项匹配成功
								result[i].append((char)(j+1+'a'));	
								flag = false;
							}
							else 
								find(j+1,info,num+1,i,len);
						}
					}
				}
			}
			return flag;
		}
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int textLines = cin.nextInt();//文档行数
		int selectors = cin.nextInt();//匹配串数
		cin.nextLine();//非常重要,如果想要在nextInt()后读取一行,就得在nextInt()之后额外加上cin.nextLine()
		//保存匹配到的文档行数序列串
		StringBuilder[] result = new StringBuilder[selectors];//数组元素并未真正创建
		String[] text = new String[textLines+selectors];//获取全部输入
		for(int i=0;i<text.length;i++) {
			text[i] = cin.nextLine();
		}
		Search sc = new Search(textLines,text,result);//创建类对象
		for(int i=0;i<selectors;i++) {
			result[i] = new StringBuilder();//创建数组元素对象
			String[] info = text[i+textLines].split(" ");//分割选择器,获取标签+id
			int len = info.length;//标签+id总长
			sc.find(0,info,0,i,len);
			sc.flag = true;		
		}
		
		for(StringBuilder sp:result) {//打印每一个匹配项的匹配结果
			
			String str = sp.toString();
			System.out.print(str.length()+" ");
			if(0 != str.length()) {	//有匹配结果		
				for(int i=0;i<str.length();i++) {
					System.out.print(str.charAt(i)-'a'+" ");//字符还原为序号
				}
			}
			System.out.println();
		}
		cin.close();
	}

}
/*
输入:
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
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值