[csp201809-3] 元素選擇器(模擬)

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Sample input

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

Sample output

3 6 9 11
1 6
0
2 9 11
1 11

解題思路

數據類型

首先是節點的結構體,很簡單:

struct node{
    node(){
        key.clear(); id.clear(); parent=0;
    }
    string key;//標籤選擇器
    string id;//id選擇器
    int parent;//父親
};
node a[maxn];

這個結構體中只存儲了當前行的標籤和id,還有一個父親節點。a[i]表示行號爲ii的節點,因此結構體中不需要存儲行號。

然後很重要的一個map

map<string,vector<int>> mp;

這個map存儲了一個string對應的行號是多少,由於標籤可能有多個相同的,所以用一個string對應一個vector。當然,這個map中也存儲了id,不過vector的size=1。

還有這個數組:

int level[maxn];

這個數組存儲了上一個第ii層的節點的行號,什麼意思呢,看下面這個示例:

html
..head
....title
..body
....h1

搜到html的時候,定爲0級,沒有父親節點,同時level[0]=1;搜到head,這是第1級,父親節點就是level[1-1]=1,同時level[1]=2;搜到title,這是第2級,父親節點就是level[2-1]=2,同時level[2]=3;然後搜索到body,這是第1級,父親節點是level[1-1]=1,同時level[1]=4;搜索到h1,這是第2級,父親是level[2-1]=4,同時level[2]=5;依次類推,我們可以確定父子關係。

搜索過程

搜索過程十分簡單,如果我們搜索div div p,那麼首先通過map找到所有p對應的行號,也就是節點的序號,然後遍歷這些序號,通過這個節點,搜索向上父親節點,直到搜索到根節點或者三個標籤都找到了,那麼這個序號就是正確的,存起來,遍歷下一個序號即可。

搜索標籤選擇器和id選擇器的方法與搜索後代選擇器完全相同。

PS:不要忘了標籤大小寫不敏感,id大小寫敏感

完整代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <sstream>
using namespace std;

const int maxn=105;
struct node{
    node(){
        key.clear(); id.clear(); parent=0;
    }
    string key;//標籤選擇器
    string id;//id選擇器
    int parent;//父親
};
node a[maxn];
int n,m,cnt;
map<string,vector<int>> mp;//key,id對應的節點號們
int level[maxn];//上一個第i層的節點號
void SplitAndBuild(string _s){//分割並且構建樹
    int _level=0,_temp; string _key,_id; _key.clear(); _id.clear();
    for (int i=0; i<_s.size(); i++){
        _temp=0;
        while(_s[i]=='.'){//尋找等級
            _temp+=1; i+=2; _level=_temp;
        }
        if(_temp){
            i--; continue;
        }
        if(_s[i]==' '){
            _key=_id; _id.clear(); continue;
        }
        _id+=_s[i];
    }
    if(_key.empty()){
        _key=_id; _id.clear();
    }
    for (int i=0; i<_key.size(); i++){//key大小寫不敏感,全都轉化爲小寫
        if(_key[i]>='A' && _key[i]<='Z') _key[i]+=32;
    }
    a[++cnt].key=_key; a[cnt].id=_id; level[_level]=cnt;//創建新的節點
    mp[_key].push_back(cnt); if(!_id.empty()) mp[_id].push_back(cnt);//加入map
    if(_level) a[cnt].parent=level[_level-1];//構建父子關係
}
bool backtrack(int nodeindex,vector<string> _v,int index){
    if(index==-1) return true;//提前搜索結束
    if(nodeindex==0) return index==-1;//搜索到根節點結束
    if(a[nodeindex].id==_v[index] || a[nodeindex].key==_v[index])
        return backtrack(a[nodeindex].parent,_v,index-1);
    else return backtrack(a[nodeindex].parent,_v,index);
}
void find(string _s){
    vector<string> v; v.clear();
    string stemp; stemp.clear();
    stringstream ss(_s);
    while(getline(ss,stemp,' ')){//分割,這個知識點可以看我的博客:https://blog.csdn.net/weixin_43347376/article/details/105134445
        if(!stemp.empty()) {
            if(stemp[0]!='#'){
                for (int i=0; i<stemp.size(); i++){//標籤大小寫不敏感
                    if(stemp[i]>='A' && stemp[i]<='Z') stemp[i]+=32;
                }
            }
            v.push_back(stemp);
        }
    }
    vector<int> line; line.clear();
    for (int i=0; i<mp[v[v.size()-1]].size(); i++){//從下向上搜索
        if(backtrack(mp[v[v.size()-1]][i],v,v.size()-1))
            line.push_back(mp[v[v.size()-1]][i]);
    }
    printf("%lu",line.size());
    for (auto it:line) printf(" %d",it);
    putchar('\n');
}
int main(){
    scanf("%d %d",&n,&m); getchar();
    for (int i=1; i<=n; i++){
        string s; getline(cin,s);
        SplitAndBuild(s);
    }
    for (int i=1; i<=m; i++){
        string s; getline(cin,s);
        find(s);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章