201809-3 CCF element selector full score solution (super detailed comment code) + problem solving ideas (super detailed)

Problem Description

insert image description here

problem solving ideas

According to the meaning of the question, we can know that there are two situations in the query.
The first is to query a label selector or id selector (which can be called a first-level query )
. The second is that there are more than two levels of queries (which can be called multiple level query )

Obviously, the first type of query needs to store all occurrences of each element in the content, and the corresponding data structure can be unordered_map<string, vector<int>>

For the second type of multi-level query, for example, to query all positions that satisfy ABCD,
first find out all the positions where D appears, that is, the vector array stored in the above map.
Traversing this array is equivalent to traversing every path ending with D.
For For each path ending with D, start backtracking from D, each time backtracking to the parent row of the current row (here a p array is required to record the position of the parent row ), and if there is an element in the row in the path that is the same as the last one of the query Element matching (this matching requires a map to record which elements are in each row, and the corresponding data structure can be unordered_map < int, unordered_map < string, int > > ), then the query element pops up
when the path ending with D is traversed, and the query The element in is also empty, it means that this path can satisfy the query, then save the answer

As for the value in the p array, it can be obtained by using an array to record the nearest starting line before each line. You can see the code for details. It is not difficult to understand that as for the values ​​in the two maps, it mainly uses
functions such as double pointers and substr. The specific visible code is not difficult to understand


Code

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <unordered_map>

using namespace std;
const int N = 110;

vector <string> text;
int n, m;
int p[N];
unordered_map <string, vector <int>> val; //存储每一个元素对应的行号
vector<vector <string>> query; //存储所有的查询
unordered_map <int, unordered_map<string, int>> value; //存储每一行有哪些元素,相当于一个可哈希的二维数组

//将每一行的父亲行存入p数组,并且去除每一行前面的.
void workparent()
{
    
    
    //先将每一行开头的点数存储在p数组中,并去除.
    for (int i = 1; i <= n; i ++)
    {
    
    
        string str = text[i];
        int j = 0;
        while (j < str.size() && str[j] == '.') j ++; //j停在第一个不是.的位置
        text[i] = str.substr(j); //去除前面的点
        p[i] = j; //保存第i行内容前面.的个数
    }
    
    //从头遍历p数组,更新p数组,将p数组存储第i行的父亲行,用t数组存储最近的有p[i] - 2个.的位置
    int t[N];
    memset(t, 0, sizeof(t));
    for (int i = 1; i <= n; i ++)
    {
    
    
        t[p[i]] = i; //更新最近的一个有p[i]个点的位置
        if (p[i] == 0) continue;
        int f = t[p[i] - 2]; //父亲节点是最近的有p[i] - 2个点的位置
        p[i] = f; //存储第i行元素的父节点行数
    }
}
 
//将每一行询问根据空格分隔存储
void workquery()
{
    
    
    for (int i = n + 1; i <= n + m; i ++) //读入的询问行
    {
    
    
        string str = text[i];
        vector <string> r;
        for (int j = 0; j < str.size(); j ++)
        {
    
    
            int k = j;
            string t;
            while (k < str.size() && str[k] != ' ') t += str[k ++]; //k越界或k是空格
            if (t[0] != '#') transform(t.begin(), t.end(), t.begin(), ::tolower); //可以转包含数字的串!
            r.push_back(t); //将每一个元素存入r
            j = k;
        }
        query.push_back(r);
    }
}

//存储每一行元素的对应行数
//存储每一行对应有哪些元素
void workpos()
{
    
    
    for (int i = 1; i <= n; i ++)
    {
    
    
        string str = text[i];
        for (int j = 0; j < str.size(); j ++)
        {
    
    
            int k = j;
            string t;
            while (k < str.size() && str[k] != ' ') t += str[k ++]; //k越界或k是空格
            if (t[0] != '#') transform(t.begin(), t.end(), t.begin(), ::tolower);
            val[t].push_back(i); //存储t元素的对应行数i
            value[i][t] = 1;//存储i行有元素t
            j = k;
        }
    }
}

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

    getchar();
    text.push_back("*"); //使下标从1开始
    for (int i = 0; i < n + m; i ++) //读入所有内容包括询问
    {
    
    
        string str;
        getline(cin, str);
        text.push_back(str); //1 ~n, n + 1 ~n + m
    }

    workparent(); //找到所有行的父亲行
    workpos(); //存储每一行元素对应的行数
    workquery(); //将询问处理并分隔

    //进行询问的查询
    for (int i = 0; i < query.size(); i ++)
    {
    
    
        vector <string> q = query[i];
        vector <int> r = val[q.back()]; //r包含最后一个元素出现的所有位置
        if (q.size() == 1) //一代
        {
    
    
            cout << r.size(); //是0的话就不会输出!
            for (auto x : r) cout << " " << x;
        }
        else //多代
        {
    
    
            vector <int> res;
            for (auto pathend : r) //遍历每一条路
            {
    
    
                vector <string> flag = q; //标记数组存储的是一行查询的元素,如果路径上出现数组中的末尾元素,就将末尾元素弹出
                for (int row = pathend; row != 0 && !flag.empty(); row = p[row]) //结束条件,标记数组空了或者路走到了尽头
                {
    
    
                    if (value[row][flag.back()]) flag.pop_back();
                }
                if (flag.empty()) res.push_back(pathend); //表明以pathend结尾的路径上能满足该行询问,将这个元素位置加入答案中
            }
            cout << res.size();
            for (auto x : res) cout << " " << x;
        }
        cout << endl;
    }
    return 0;
}

Guess you like

Origin blog.csdn.net/qq_51800570/article/details/129387448