您的位置:首页 > 其它

简单树匹配(STM)及其应用举例

2015-12-19 17:17 926 查看
(一)简单树匹配

简单树匹配(Simple Tree Matching,  STM)的目标是找到两棵树间的最大匹配。两棵树间的最大匹配定义如下:

设A和B是两棵树,而i和j分别是A和B中的两个节点。两棵树间的一个匹配定义为一个映射M,使得对每一个节点对(i,j)属于M(这里i和j都不是根节点),都有(parent(i),parent(j))属于M。其中,拥有最多节点对的匹配被称为A和B之间的最大匹配。需要注意,最大匹配可能不止一个。

用W(A, B)表示树A和树B之间的简单树匹配中节点对的个数,它的形式化定义如下:



其中,RA和RB分别是树A和树B的根节点,Ak是树A的第一层子树的第k个节点,Bn是树B的第一层子树的第n个节点,m(<A1,...,Ak>,<B1,...,Bn>)是指树A的第一层子树和树B的第一层子树的最大匹配中节点对的个数。W(A,
B)的形式化定义是说,当树A和树B的根节点不同时,W(A, B)等于0;当树A和树B的根节点相同时,W(A,
B)等于树A的第一层子树和树B的第一层子树的最大匹配中节点对的个数加1。我们还需要对m(<A1,...,Ak>,<B1,...,Bn>)作进一步的定义。

先看m(<A1,...,Ak>,<B1,...,Bn>)的形式化定义:

m(<>, <>)= 0 // <>代表一个空的子树列表

m(s,<>)= m(<>, s)= 0 // s匹配任意非空子树列表

m(<A1,...,Ak>,<B1,...,Bn>)=max(

m(<A1,...,Ak-1>,<B1,...,Bn-1>)
+ W(Ak,Bn),

m(<A1,...,Ak>,<B1,...,Bn-1>),

m(<A1,...,Ak-1>,<B1,...,Bn>)



这个形式化定义的具体含义是:

(1)当两个子树列表任意一个为空时,匹配数为0;

(2)两个子树列表的最大匹配可以看作第一个子树列表的前k个子树组成的子树列表和第二个子树列表的前n个子树组成的子树列表的最大匹配;这时,我们有三个选择:

a.匹配Ak和Bn,这时m(<A1,...,Ak>,<B1,...,Bn>)变为m(<A1,...,Ak-1>,<B1,...,Bn-1>)
+ W(Ak,Bn);

b.不匹配Bn,这时m(<A1,...,Ak>,<B1,...,Bn>)变为m(<A1,...,Ak>,<B1,...,Bn-1>);

c.不匹配Ak,这时m(<A1,...,Ak>,<B1,...,Bn>)变为m(<A1,...,Ak-1>,<B1,...,Bn>)。

然后,从这三个中选出最大的那个,就得到最大的m(<A1,...,Ak>,<B1,...,Bn>)了。我们可以看出,求m(<A1,...,Ak>,<B1,...,Bn>)的过程和求字符串编辑距离的过程是很相似的,实际上,我们同样可以通过动态规划的方法求m(<A1,...,Ak>,<B1,...,Bn>),并且同样可以通过得到二维数组(这里是m)对齐两个子树列表,由于在从字符串编辑距离到字符串对齐一文中已有详细阐释,这里就不再讲动态规划的具体实施过程和对齐子树列表了。不过,求m(<A1,...,Ak>,<B1,...,Bn>)也有它的特有之处,那就是里面会递归计算W(Ak,Bn)。

简单树匹配涉及到的概念都讲完了,这里给出它的一个实现算法及我的Java实现。

算法:



Java实现:

/**
* 简单树匹配算法实现;
* @param A
* @param B
* @return
*/
public int simpleTreeMatching(Element A, Element B) {
String aTag = A.tagName();
String bTag = B.tagName();
if (!aTag.equals(bTag)) {
return 0;
}
// 只有标签节点,不包含文本节点
Elements aChild = A.children();
Elements bChild = B.children();
int aChildNum = aChild.size();
int bChildNum = bChild.size();
if (aChildNum == 0 || bChildNum == 0) {
return 1;
}

int[][] m = new int[aChildNum + 1][bChildNum + 1];
int[][] w = new int[aChildNum + 1][bChildNum + 1];
// 当A没有子树时,只有0个匹配
for(int i = 0; i < aChildNum + 1; i++) {
m[i][0] = 0;
}
// 当B没有子树时,只有0个匹配
for(int j = 0; j < bChildNum + 1; j++) {
m[0][j] = 0;
}
for(int i = 1; i < aChildNum + 1; i++) {
for(int j = 1; j < bChildNum + 1; j++) {
w[i][j] = simpleTreeMatching(aChild.get(i - 1), bChild.get(j - 1));
m[i][j] = Math.max(Math.max(m[i][j-1], m[i - 1][j]),
m[i-1][j-1] + w[i][j]);
}
}
return m[aChildNum][bChildNum] + 1;
}
(实现中用到了jsoup)

最后,我们还可以利用简单树匹配来定义两棵树之间的相似度:

STM(A, B)/((nodes(A) + nodes(B))/ 2)

其中,nodes(X)表示树X中的节点个数。

(二)简单树匹配的应用例子

简单树匹配可以用于从新闻列表页中挖掘出数据区域——也就是包含指向新闻详情页链接的区域。也就是说,我们有这样一个页面:

http://politics.people.com.cn/GB/1027/index.html

可以从中找到这个区域:



为了给出利用简单树匹配挖掘新闻列表页数据区域的算法,我们先来观察上面这个新闻列表页的网页源码。观察它的源码,我们得到这样的结果:

(1)每个详情页链接包含在一个单独的标签节点中:

·<a href='/n1/2015/1218/c1001-27947561.html' target="_blank">湖南邵阳公安局禁毒支队长被查 该局涉案毒品曾遭盗卖</a>
·<a href='/n1/2015/1218/c1001-27946716.html' target="_blank">三部门印发公务员面试办法 要全程录像或录音</a>
·<a href='/n1/2015/1218/c1001-27946688.html' target="_blank">西安宣告实现贫困人口全部脱贫</a>
·<a href='/n1/2015/1218/c1001-27946581.html' target="_blank">习主席都去的展览你还不关注?这些科技绝对让你尖叫</a>
·<a href='/n1/2015/1218/c1001-27946339.html' target="_blank">十八大以来落马"大老虎"19人获刑 仅1人低于10年  </a>
·<a href='/n1/2015/1218/c1001-27946190.html' target="_blank">国务院批复同意西宁市城市总体规划</a>
(2)这些包含详情页链接的节点拥有共同的父节点

(3)这些包含详情页链接的节点的兄弟较多

(4)这些包含详情页链接的节点之间相似度高

(5)这些包含详情页链接的节点是相邻的

有了这些观察结果,我们可以很容易想到从中挖掘出数据区域的算法:先根遍历网页的DOM树,对遇到每一个节点,我们得到它的第一层孩子节点,然后从左到右计算相邻两个孩子节点的相似度(就是上面给出的两棵树的相似度),根据设置相似度阈值以及数据区域最少兄弟节点个数等限制,从中挖掘出数据区域,没有被当作数据区域的节点继续递归处理,而被当作数据区的节点不再对其子节点进一步处理,当遍历完整棵树后,可能得到多个数据区域,我们从中选出兄弟最多的那个数据区域,那便是我们想找的。

最后总结一下,简单树匹配用于找到两棵树间的最大匹配;我们还可以通过简单树匹配定义两棵树之间的相似度;利用两棵树间的相似度,我们从新闻列表页中挖掘出了我们想要的数据区域。

参考书籍:

《Web数据挖掘》第二版,作者:Bing Liu,译者:俞勇
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息