程序设计思维与实践 Month4 模拟

CSP 201809-3 元素选择器

1. 问题描述

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

在这里插入图片描述

2. 思路历程

  • 这题也是一道没有用到什么算法的模拟题(可惜我还是觉得挺困难)。先确定模拟的步骤:先读入结构化文档,再为每一个选择器进行选择,最后为每一个选择器输出。
  • 对于读入结构化文档,采取按行读入的方法,getline函数一次读入一整行,再用split()函数对这个字符串进行分割。
  • 对于为每一个选择器进行选择,由于标签对大小写不敏感,且双方只有用同一种写法时才能比较,因此用tolower函数将所有字母变为小写,便于后面的比较。对于满足条件的选择器,直接加入vector ans。
  • 对于为每个选择器输出,直接输出ans的长度和其中的元素即可。

3. 具体实现

  • 在split()函数中,先遍历整个字符串找到第一个非 . 元素位置(即标签出现的开头位置)、空格的位置(即属性出现的开头位置),标志量flag为2,表示有属性,否则没有属性。
    这里用到了substr()函数获取子字符串,是一个很好用的函数
  • 对于判断三种选择器的条件,标签选择器和id选择器比较简单,直接查询比较即可,但是后代选择器需要另外的函数isMatch来另外判断。在isMatch中主要考虑逐级往上查找选择器的祖先。
  • 千万注意,在每次处理完一个选择器之后都要清空数组(我在这里错了好久都找不到原因

4. 代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <string.h>
#include <sstream>
using namespace std;
const int maxn = 111;

struct Element
{
    string label, id;
    int level;
} elements[maxn];

int m, n;

vector<string> qus; // 保存查询
vector<int> ans; // 保存答案

void toLower(string &str)
{
    for (int i = 0; i < str.size(); i++)
    {
        if (str[i] >= 'A' && str[i] <= 'Z')
            str[i] = tolower(str[i]);
    }
}

void split(string &str, int x)
{
    int flag = 0, s1 = 0, s2 = 0, s = 0;
    string label, id;
    
    for (int i = 0; i < str.size(); i++)
    {
        if (str[i] == '.') s++;
        if (str[i] != '.' && flag == 0) // 第一个非 . 的位置
        {
            flag = 1;
            s1 = i;
        }
        if (str[i] == ' ' && flag == 1) // 标记属性出现的位置
        {
            s2 = i + 1;
            flag = 2;
            break;
        }
    }
    
    if (flag != 2) // 没有属性
    {
        label = str.substr(s1);
        toLower(label);
        elements[x].label = label;
        elements[x].level = s / 2;
    }
    else // 有属性
    {
        label = str.substr(s1, s2 - s1 - 1);
        toLower(label);
        id = str.substr(s2);
        elements[x].label = label;
        elements[x].id = id;
        elements[x].level = s / 2;
    }
}

// 通过检查元素的祖先判断元素与选择器是否匹配
bool isMatch(int x)
{
    int t = elements[x].level;
    long sz = qus.size() - 2;
    for (int i = x - 1; i >= 0; i--)
    {
        if (elements[i].level == t - 1)
        {
            if (qus[sz][0] != '#' && qus[sz] == elements[i].label) sz--;
            else if (qus[sz][0] == '#' && qus[sz] == elements[i].id) sz--;
            t = elements[i].level;
            if (sz < 0) return true;
        }
    }
    return false;
}

int main()
{
    cin >> n >> m;
    getchar();
    
    ans.clear();
    qus.clear();
    
    string s;
    for (int i = 0; i < n; i++)
    {
        getline(cin, s);
        split(s, i);
    }
    
    for (int i = 0; i < m; i++)
    {
        getline(cin, s);
        stringstream ss;
        ss << s;
        string st;
        while (ss >> st)
        {
            if (st[0] != '#') toLower(st); // 不是id选择器,可以全部转化成小写
            qus.push_back(st);
        }
        long sz = qus.size();
        for (int j = 0; j < n; j++)
        {
            if (qus[sz - 1][0] == '#' && qus[sz - 1] == elements[j].id || qus[sz - 1][0] != '#' && qus[sz - 1] == elements[j].label)
            {
                if (sz == 1 || isMatch(j)) ans.push_back(j + 1);
            }
        }
        cout << ans.size();
        for (int i = 0; i < ans.size(); i++) cout << " " << ans[i];
        cout << endl;
        
        ans.clear();
        qus.clear();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值