思路:
这是一个树,可以类比数据结构中的二叉树。
二叉树有一个值(val),和不超过两个叶子节点
这个html树可能有多个值,id属性和 标签属性(div/p/h1这些),可能有多个节点
题目描述:
可能出现 div #one 这种复合的后代选择器
于是我想直接用python嵌套的字典来完成
一:节点的表示:每个节点用dict表示,每个节点的关键属性是,dict['idTag'] id ,dict['value'] 标签属性 ,dict['childNode'] //关键,是列表类型,列表里仍然储存着 节点(递归)
二:创建的罗技逻辑:
creat (root)函数:
为了实现它,首先定义了一个linePointet【int】,指向待处理的html的第几行
creat (root),可以把缓冲区(html字符串的第linePointer个)的节点,挂载在root下
因此首先需要创建一个最大的,把html当作跟放进去,下面的语句起到这个作用
htmlDom = dict()
htmlDom['leval']=0
htmlDom['value']=None
然后呢,就可以递归的创建了,逻辑是这样的:
循环 一直认为当前缓冲区的是 root的子节点{
把缓冲区的字符串处理成定义的标准类型
IF这个新的节点是root的跟{
1:把新节点(newNode)append到root的儿子列表里
2:调整下linePointer到下一个节点,注意,下一个可能是newNode的儿子,也可能是newNode的兄弟,也可能是是 newNode直接间接前驱,但是注意,只要 linePointer指的不是当前递归调用里的,当前递归调用层只会退出,直到linePointer指的确是是当前递归栈里的儿子
3:递归调用creat (newNode),也就是 认为新节点是新的跟,这样可以处理多层嵌套
}
ELSE{退出}
}
三:查询的逻辑,和创建有类似之处,参考注释即可
四:创建好的dict长什么样,以给出的样例为例子
样例输入:
11 0
html
..head
....title
..body
....h1
....p #subtitle
....div #main
......h2
......p #one
......div
........p #two
{
"leval": 0,
"value": null,
"childNum": 1,
"childNode": [{
"leval": 1,
"idTag": null,
"value": "html",
"lineNum": 1,
"childNum": 2,
"childNode": [{
"leval": 2,
"idTag": null,
"value": "head",
"lineNum": 2,
"childNum": 1,
"childNode": [{
"leval": 3,
"idTag": null,
"value": "title",
"lineNum": 3,
"childNum": 0,
"childNode": []
}]
}, {
"leval": 2,
"idTag": null,
"value": "body",
"lineNum": 4,
"childNum": 3,
"childNode": [{
"leval": 3,
"idTag": null,
"value": "h1",
"lineNum": 5,
"childNum": 0,
"childNode": []
}, {
"leval": 3,
"idTag": "subtitle",
"value": "p",
"lineNum": 6,
"childNum": 0,
"childNode": []
}, {
"leval": 3,
"idTag": "main",
"value": "div",
"lineNum": 7,
"childNum": 3,
"childNode": [{
"leval": 4,
"idTag": null,
"value": "h2",
"lineNum": 8,
"childNum": 0,
"childNode": []
}, {
"leval": 4,
"idTag": "one",
"value": "p",
"lineNum": 9,
"childNum": 0,
"childNode": []
}, {
"leval": 4,
"idTag": null,
"value": "div",
"lineNum": 10,
"childNum": 1,
"childNode": [{
"leval": 5,
"idTag": "two",
"value": "p",
"lineNum": 11,
"childNum": 0,
"childNode": []
}]
}]
}]
}]
}]
}
def creat(root):
#功能是,把缓冲区(html字符串的第linePointer个)的节点,挂载在root下
root['childNum'] = 0
root['childNode']=list()
while True:
global linePointer
if linePointer>=n:
return
line = allData[linePointer]
newNode=dict()
newNode['leval']=int(line.count(".") / 2) + 1
line=line.replace(".","")
valueAndId=line.split()
if len(valueAndId)==1:
newNode['idTag']=None
else:
newNode['idTag']=valueAndId[1].replace("#",'')
newNode['value']=valueAndId[0].lower()
newNode['lineNum']=linePointer+1
if newNode['leval']>root['leval']:
root['childNum']+=1
root['childNode'].append(newNode)
linePointer+=1
creat(newNode)
else:
#不是当前root的儿子,直接退出,linePointer指向的字符串 交给他真正的爸爸解决把
return
## 选择器
def dfs(query, keyleval,node,lineArr):
#query:list是查寻字符串的列表[div,div] ,keyleval:int,表示查询到query的第几层,node:dict,指的是这层递归要比较的节点,也就是要查看query[keyleval]是不是等于node的相关值
isId=query[keyleval].startswith('#')
if isId:
key='idTag'
rightVal=query[keyleval].replace('#','')
else:
key='value'
rightVal=query[keyleval].lower()
if rightVal==node.get(key):
#这个node符合当前查寻得这一层
if keyleval>=len(query)-1:
#是最后一个节点,递归的时候仍然不修改keyleval
lineArr.append(node['lineNum'])
for i in node['childNode']:
dfs(query, keyleval , i, lineArr)
else:
#不是最后一个节点,keyleval要+1,查询query的下一层
for i in node['childNode']:
dfs(query,keyleval+1,i,lineArr)
else:
#这层不是,看这层的儿子符合不符合
for i in node['childNode']:
dfs(query, keyleval, i, lineArr)
n, m = list(map(int, input().split()))
allData=list()
for i in range(n):
allData.append(input())
htmlDom = dict()
htmlDom['leval']=0
htmlDom['value']=None
#重要,指示下一个要创建节点的下标
linePointer=0
creat(htmlDom)
for i in range(m):
lineArr=list()
query=input().split()
#lineArr储存的是,符合条件的行号
dfs(query,0,htmlDom,lineArr)
lineArr.insert(0, len(lineArr))
for i in lineArr:
print(i, end=' ')
print()