Month4--元素选择器

题目原文

题目背景
题目描述
输入输出
样例解释

解题思路

建立结构体存储每个元素的标签、id、层级和父元素指针。其中父元素指针用于在后代选择器中判断父元素是否符合要求。

首先将元素文档中的信息读入,并根据前面‘.’的数量判断该元素的层级,点的数量除以2即该元素层级(其实可以不除,因为只用到了层级的大小关系),再根据层级以及元素栈找到元素的父元素。

之后读入一行并根据空格分割,将分割出的属性放在sel数组中;如果只有一个属性那么判断完那一个属性后就完毕;如果有多个属性,判断最后一个属性是否与当前元素属性一致,如果一致就继续去判断父节点的属性,直到没有父节点或者属性都匹配完,如果属性都匹配完那么这个元素就是要找的元素。

需要注意的是,在id属性中是需要区分大小写的,而在标签属性中不需要区分大小写,所以在判断时候应该分开处理。
因为在判断的时候选择器中一样带有#标志,所以可以将id属性的#算入id属性中。

实现代码

#include <iostream>
#include <sstream>
#include <vector>
#include <stack>
#include <cstring>

using namespace std;

const int maxn = 101;

struct node {
    int level;      //层级
    string name;    //标签属性
    string id;      //id属性
    node* parent;       //父标签
    void val(string id, string lab, int level) {
        this->name = id;
        this->id = lab;
        this->level = level;
        this->parent = NULL;
    }
} ele[maxn];

void divide(const string& line, vector<string>& sel)
{//将读入的一行line分割为每段元素,放入sel中
    sel.clear();
    string token="";
    for (int i = 0; i < line.size(); i++)
    {
        if (line[i] == ' ')
        {
            sel.push_back(token);
            token="";
        }
        else token += line[i];
    }
    sel.push_back(token);
}

bool check(node* t, const string& s)
{
    if (s[0] == '#') return s == t->id;
    if (s.size() != t->name.size()) return false;
    for (int i = 0; i < s.size(); i++)
    {
        if (tolower(s[i]) != tolower(t->name[i])) return false;
    }
    return true;
}

int main()
{
    int n, m;
    cin >> n >> m;
    getchar();

    stack<node*> parents;//使用栈结构记录元素,用于计算层级以及父元素

    //读入元素文档
    for (int i = 1; i <= n; i++)
    {
        string line;
        getline(cin, line);

        int level = 0;
        while (line[level] == '.') level++;//统计前面‘.’的数量计算层级

        
        string name = "", id = "";
        int k;
        for (k = level; k < line.size(); k++)
        {
            if (line[k] == ' ')//空格以前为标签属性
                break;
            name += line[k];
        }
        for (k; k < line.size(); k++) {
            if (line[k] == '#')//取除#以前内容(下次循环从#开始,井号还在)
                break;
        }
        for (k; k < line.size(); k++) {
            id += line[k];
        }
        level /= 2;//每层两个‘.’(不除也无所谓)
        ele[i].val(name, id, level);

        if (!parents.empty())//找父元素
        {
            node* p = parents.top();
            while (p->level >= level)//将层级高的出栈
            {
                parents.pop();
                p = parents.top();
            }//找到第一个层级更低的,即为其父元素
            ele[i].parent = p;
        }
        parents.push(&ele[i]);
    }

    //读入选择器
    vector<int> ans;
    vector<string> sel;
    while (m--)
    {
        string line;
        getline(cin, line);
        divide(line, sel);//将一行中的选择器分割

        ans.clear();
        for (int i = 1; i <= n; i++)
        {
            //cout << ii << ele[ii].name << ele[ii].id << endl;
            int cnt = sel.size() - 1;//从选择器最后一个属性开始比较
            if (check(&ele[i], sel[cnt]))//因为id属性带#,而标签属性不带#,可以直接分别比较sel[cnt]==ele[i].name || sel[cnt] == ele[i].id
            {//如果选择器中最后一个属性符合,依次比较父元素和前面的属性
                node* p = ele[i].parent;
                cnt--;
                while (p!=NULL && cnt >= 0)
                {
                    if (check(p, sel[cnt]))//sel[cnt] == p->name || sel[cnt] == p->id
                        cnt--;//如果仍然符合继续比较前一个属性
                    p = p->parent;
                }
                if (cnt == -1) //cnt=-1表示比较完了所有属性(sel数组中从0~size-1存的)
                    ans.push_back(i);//放入答案中
            }
            
        }

        cout<< ans.size();
        for (int i = 0; i < ans.size(); i++)
            cout<<" "<<ans[i];
        cout << endl;
    }
    return 0;
}
/*
11 100
html
..head
....title
..body
....h1
....p #subtitle
....div #main
......h2
......p #none
......div
........p #two
p
#subtitle
h3
div p
div p p
*/

小结

写题时需要先通读题目,理解大概的解题思路,确定结构体中的变量,不然在后面会发现很多变量没有定义,导致来回改动代码;
另外在题目很长的时候需要注意很多题目中的细节,比如后代选择器可以有多个属性而不是两个,id属性大小写敏感而标签属性大小写不敏感。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。