这类问题归结为:在一个目标文本字符串中找出指定的多个词表串,一般应用在文本过滤中
confilter为内容过滤模块,词表匹配简单概括为:查找帖子中包含的所有过滤词。
举例:
过滤词表:{he,she,his,hers}
帖子:ushers
此帖子中含有的过滤词为:she,he,hers
for(i=0; i<content_len; i++){
if(以content+i为起始的字符串的前三位不在bitmap中)continue;
根据content[i] content[i+1]从LengthMap中得到len_list
foreach ( len in len_list){
将长为len的字符串签名,并查找odict。
}
}
举例:
过滤词表:{he,she,his,hers}
帖子:ushers
由上图可知:
trie树又名字典树、单词前缀树,是以空间换时间的字符树,能够一个字符一个字符处理,利用字符串前缀来节省内存空间。
举例:
词表{abcd , abc , abd , b , bcd , efg , hij},用trie树存储如下,其中红色节点为词的终点:
时间复杂度:o(n)
时间复杂度:o(p_len)
时间复杂度:o(p_len)
时间复杂度:o(m+n)
词表:{ai , se , as}
字符集:{a , i , s , e , a , s}:不是只字符种类集合
空间复杂度为:o(L*X):即随词表长度线性增长。
26i
ac自动机是基于trie树的经典的多模匹配算法。在trie树基础上增加失败指针,避免回溯,降低了匹配的复杂度。
举例:设词表为{say,she,shr,he,her}:
利用树的宽度优先搜索及队列,构造节点X的失败指针时,沿着X的父节点的失败指针走,直到有一个节点,其儿子节点Y和X的关键字相同,则X的失败指针指向Y。
模式串在trie树上匹配时,若与当前节点的关键字不能匹配,去当前节点的失败指针指向的节点继续匹配,从而避免回溯。
时间复杂度:o(n)
时间复杂度:o(n)
时间复杂度:o(m+z) ,其中z为出现在模式串中的词表中的词的个数
同trie树
解决了trie树的回溯问题,但是内存问题仍旧没有解决。
tst为一棵三叉树,其结合了digital-tries的时间效率和binary-search-tree的空间效率。一个节点有left指针、middle指针、right指针,匹配字符串时,当前字符和TST当前节点比较:小于,则沿着left指针往下走;相等,则沿着middle指针往下走,且找到一个匹配串;大于,则沿着right指针往下走。
举例:
词表{as, at, be, by, he, in, is, it, of, on, or, to}
tst图如下:
设树的高度为h,模式串:T[1…m]
设词表的字符集为L(词表中词的所有字符,可重复,不是字符种数)
空间复杂度为:o(L)
匹配效率依赖于树的高度,可以在建tst之前,将词组排序,二分建树,使得tst为平衡树,提高匹配的时间效率。
资源词表一共18个,词表长度小于1000的为6个,词表长度为1000-7000的为9个,词表长度为1.2万左右的为3个。经分析,词表长度都不是很大,大多数词表的过滤词长度分布比较集中,可以根据过滤词长度分布估计出该词表的平均字符个数。便于分析算法的时间及空间效率。
贴吧confilter的缺点在于回溯且没有考虑过滤词前缀关联,trie树的缺点为回溯且内存占用多,改进内存分配方式,访问节点的子节点时间复杂度增高,ac自动机的缺点同trie树的内存问题。改进内存的ac自动机能节省内存,又带来了查找节点子节点的开销。tst均衡考虑了时间、空间复杂度,但时间复杂度高于ac自动机。在此,只能理论分析各个算法的时间、空间复杂度,优点、缺点。“实践出真知”究竟哪种算法适用于贴吧,需要根据贴吧词表进行实地测试。在内存使用及匹配的时间效率上进行对比,才能确定最好或接近最好的方案。不同的算法适用不同的词表,也可以考虑配置算法:不同词表不同的算法。考虑到扩展性:当前过滤的字符为“GBK编码的字符”,兼容unicode、utf-8需要进一步考虑或者预留。
附:
采用的是贴吧现有的词表,词表长度分别取99(1984.txt)、431(bword.txt)、2802(a.txt)、16898(chinese)、42151(chinese+english)、150953(white_user_zhaohui.txt),all.txt(234382),其中chinese为包含了多个中文词表的文件夹,english为包含了多个英文词表的文件夹。从mis中提取了128397个查询输入串。算法本身直接从文件中取查询输入串。主要统计3种算法初始化时间、对于所有查询输入串的查询时间、内存消耗、cpu使用率。
词表名称 |
词表行数 |
初始化时间 (单位ms) |
查询时间 (单位ms) |
cpu占有率 |
实际内存 (单位M) |
1984.txt |
99 |
0.0 |
1780 |
||
bword.txt |
431 |
0.0 |
1890 |
||
a.txt |
2802 |
0.0 |
7070 |
1.03 |
|
chinese.txt |
16898 |
10.0 |
12320 |
6.67 |
1.28 |
chinese_english.txt |
42151 |
20.0 |
20870 |
8.33 |
3.09 |
white_user_zhaohui.txt |
150953 |
80.0 |
36700 |
28.28 |
6.69 |
all.txt(所有词表的合并文件) |
234382 |
140.0 |
94530 |
25 |
11.34 |
词表名称 |
词表行数 |
初始化时间 (单位ms) |
查询时间 (单位ms) |
cpu占有率 |
实际内存 (单位M) |
1984.txt |
99 |
0.0 |
950 |
||
bword.txt |
431 |
0.0 |
980 |
||
a.txt |
2802 |
250.0 |
1200 |
||
chinese.txt |
16898 |
1550.0 |
1340 |
||
chinese_english.txt |
42151 |
4030.0 |
1580 |
3.73 |
299.61 |
white_user_zhaohui.txt |
150953 |
6880.0 |
2960 |
8.00 |
562.2 |
all.txt(所有词表的合并文件) |
234382 |
由于内存不足而终止 |
词表名称 |
词表行数 |
初始化时间 (单位ms) |
查询时间 (单位ms) |
cpu占有率 |
实际内存 (单位M) |
1984.txt |
99 |
0.0 |
1170 |
||
bword.txt |
431 |
0.0 |
1240 |
||
a.txt |
2802 |
180.0 |
2150 |
||
chinese.txt |
16898 |
1570.0 |
2830 |
9.17 |
9.42 |
chinese_english.txt |
42151 |
5270.0 |
4180 |
7.13 |
16.26 |
white_user_zhaohui.txt |
150953 |
1560.0 |
13480 |
9.17 |
55.50 |
all.txt(所有词表的合并文件) |
234382 |
7720.0 |
18000 |
9.17 |
77.11 |
对比分析:
词表长度的选取覆盖了多个数量级,根据以上数据可以得出结论:ac自动机在查找时间上有绝对的优势,但是比较耗费内存,甚至出现了no_memory。改进内存的ac自动机初始化时间高于贴吧confilter,但查找时间缩短了几倍。cpu占有率及实际内存的占用均不大。同时改进内存的ac自动机避免了语义误伤。