1. 程式人生 > >CCF-201809-3-元素選擇器

CCF-201809-3-元素選擇器

題目很長,大家自行去官網看。
第三題還是一如既往的是大模擬,模擬css元素選擇器,有接觸過前端的同學對此不陌生了吧。
以前學css的時候就想過層疊樣式表的實現,但是也沒細究。ccf第三題有出過markdown轉html的,我就預測ccf還會再出前端類的題目,那時候猜可能會是css,沒想到真的出了。這些題外話了,還是講回題目吧,這題難度不算高,但是細節決定成敗。

思路:
結點類:
包含該結點的行數,標籤名,id名,父結點(前繼結點,儲存父結點的意義是為了後面的查詢,從後往前找,比從前往後找好,你想想每個結點有多個子節點,但只有唯一父結點,從前往後要找出對應的序列得巢狀多少次迴圈啊)和子節點列表

1.整理輸入:先把輸入的結構化文件由結點連成一棵樹,每個結點要臉上樹,就看前面有多少個點(點數/2==往下的級數),從根節點出發找到對應的父結點再連上

2.查詢:每個查詢都進行一次樹的遍歷(我用的是層次遍歷),找到對應的選擇器就開始往上找父元素。注意,選擇器從後往前匹配,比如 div a h1, 就在遍歷的時候找h1元素,從h1開始匹配,再往上找父元素a, div。再注意,這裡的父元素,是所有祖先元素,往上一級找不到就繼續往上一級找,直到選擇器組都匹配完就記錄這個結點的行數,次數加1。

3.細節:標籤在結構化文件中和查詢的選擇器中大小寫都有可能,而標籤名大小寫不敏感,所以遇到標籤要化為小寫。

新改動:
之前程式碼有個很難找的BUG,才90,現在終於找到問題所在了,終於AC100。這還得感謝我的一個朋友。
問題出在我的根結點,我的根結點的標籤是“root”,理論上這個root不應該被匹配到,而且剛剛好ccf的測試用例的選擇器也有root,所以選擇上了。答案應該是0才對。解決方法:把根節點的標籤(label)名換一個就是了,乾脆空字串"".

Java程式碼:

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Scanner;

public class 元素選擇器 {
    static Scanner input = new Scanner(System.in);
    //static Node root = new Node(0, null, "root", "");  修改前 90
    static Node root = new Node(0, null, "", ""); //修改後 100

    public static void main(String[] args) {
        int n = input.nextInt();
        int m = input.nextInt();

        //接收結構化文件,並整理好結構
        input.nextLine();
        getDateToTree(n);

        //對每個選擇器進行匹配,輸出匹配結果
        printAns(m);
    }

    //接收結構化文件,並整理成樹結構
    public static void getDateToTree(int n) {
        for (int i = 0; i < n; i++) {
            String[] line = input.nextLine().split(" ");
            int dotCount = 0;
            int idx = 0;
            for (int j = 0; j < line[0].length() && line[0].charAt(j) == '.'; j++) {
                dotCount++;
                idx++;
            }

            //把標籤轉為小寫
            String label = line[0].substring(idx, line[0].length()).toLowerCase();
            String id = line.length == 2 ? line[1] : "";

            //指標每次從根結點開始找,找dotCount/2次
            Node pointer = root;
            for (int j = 0; j < dotCount / 2; j++)
                pointer = pointer.subNodes.get(pointer.subNodes.size() - 1);
            pointer.subNodes.add(new Node(i + 1, pointer, label, id));
        }
    }

    //對每個選擇器進行匹配,輸出匹配結果
    public static void printAns(int m) {
        for (int i = 0; i < m; i++) {
            //該列表儲存的是選擇器匹配到的標籤們在結構化文件中的行數
            ArrayList<Integer> lineNums = new ArrayList<>();
            String[] line = input.nextLine().split(" ");
            match(lineNums, line);
            lineNums.sort(Comparator.comparing(Integer::byteValue));
            System.out.print(lineNums.size());
            for (int lineNum : lineNums)
                System.out.print(" " + lineNum);
            System.out.println();
        }
    }

    //選擇器匹配,層次遍歷結構樹,對每個結點進行匹配
    public static void match(ArrayList<Integer> lineNums, String[] line) {
        LinkedList<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node tem = queue.poll();

            //從選擇器最底端開始匹配
            //如果選擇器是標籤就改為小寫
            String seleter = line[line.length - 1].charAt(0) == '#' ?
                    line[line.length - 1] : line[line.length - 1].toLowerCase();
            if (seleter.equals(tem.label) || seleter.equals(tem.id)) {
                int lineIdx = line.length - 2;
                Node pointer = tem.preNode;
                //從最下級開始往上找父元素
                while (pointer != null && lineIdx >= 0) {
                    //如果選擇器是標籤就改為小寫
                    seleter = line[lineIdx].charAt(0) == '#' ?
                            line[lineIdx] : line[lineIdx].toLowerCase();
                    if (pointer.label.equals(seleter) ||
                            pointer.id.equals(seleter)) {
                        lineIdx--;
                    }
                    pointer = pointer.preNode;
                }
                if (lineIdx == -1)
                    lineNums.add(tem.lineNum);
            }

            //子結點入隊
            for (Node val : tem.subNodes)
                queue.offer(val);
        }
    }

}

//標籤元素結點類
class Node {
    int lineNum;//元素所在的行數
    Node preNode;
    ArrayList<Node> subNodes = new ArrayList<>();//子結點列表
    String label;
    String id;

    Node(int lineNum, Node preNode, String label, String id) {
        this.lineNum = lineNum;
        this.preNode = preNode;
        this.label = label;
        this.id = id;
    }
}

python程式碼:

# 標籤元素結點類
class Node:
    def __init__(self, lineNum, preNode, label, id):
        self.lineNum = lineNum  # 元素所在的行數
        self.preNode = preNode  # 父節點
        self.label = label  # 標籤
        self.id = id  # id
        self.subNodes = []  # 子結點列表


# 接收結構化文件,並整理成樹結構
def get_date_to_tree(n, root):
    for i in range(n):
        line = input().split()
        dotCount = 0
        idx = 0

        for j in range(len(line[0])):
            if line[0][j] == '.':
                dotCount += 1
            else:
                idx = j
                break

        # 把標籤轉為小寫
        label = line[0][idx:len(line[0])].lower()
        id = line[1] if len(line) == 2 else ""

        # 指標每次從根結點開始找,找dotCount/2次
        pointer = root
        for j in range(int(dotCount / 2)):
            pointer = pointer.subNodes[len(pointer.subNodes) - 1]

        pointer.subNodes.append(Node(i + 1, pointer, label, id))

    # 對每個選擇器進行匹配,輸出匹配結果


def print_ans(m, root):
    for i in range(m):
        # 該列表儲存的是選擇器匹配到的標籤們在結構化文件中的行數
        lineNums = []
        line = input().split()
        match(lineNums, line, root)
        print(len(lineNums), end=' ')
        for lineNum in sorted(lineNums):
            print(lineNum, end=' ')
        print()


# 選擇器匹配,層次遍歷結構樹,對每個結點進行匹配
def match(lineNums, line, root):
    queue = []
    queue.append(root)
    while queue:
        tem = queue.pop(0)

        # 從選擇器最底端開始匹配
        # 如果選擇器是標籤就改為小寫
        seleter = line[len(line) - 1] if line[len(line) - 1][0] == '#' else \
            line[len(line) - 1].lower()

        if seleter == tem.label or seleter == tem.id:
            lineIdx = len(line) - 2
            pointer = tem.preNode

            # 從最下級開始往上找父元素
            while (pointer != None and lineIdx >= 0):
                # 如果選擇器是標籤就改為小寫
                seleter = line[lineIdx] if line[lineIdx][0] == '#' else \
                    line[lineIdx].lower()
                if (pointer.label == seleter or
                        pointer.id == seleter):
                    lineIdx -= 1
                pointer = pointer.preNode

            if (lineIdx == -1):
                lineNums.append(tem.lineNum)

        # 子節點入隊
        for val in tem.subNodes:
            queue.append(val)


# 主程式邏輯
# root = Node(0, None, "root", "") 修改前 90
root = Node(0, None, "", "") # 修改後 100
n, m = [int(val) for val in input().split()]

# 接收結構化文件,並整理好結構
get_date_to_tree(n, root)

# 對每個選擇器進行匹配,輸出匹配結果
print_ans(m, root)