敏感词过滤-AC自动机

在很多内容系统中,都需要过滤一些敏感词,比如说"fuck you shit up"就要发现里面有"fuck"、"shit"这些脏词。

首先,我们要先了解敏感词过滤的一些特征:

1. 敏感词多,一般成千上万

2. 单词长度有限,一般不会超过10

3. 要过滤的句子长度有限,一般不过1000


通过上面特征,我们粗略的算一下,如果采用暴力匹配方案的话,复杂度将会是1k*10*1k=10^7左右的运算量。


现在再考虑一下,该机制要用在什么样的系统中。如果是一个内容发布平台,每天调用次数有限,比如评论功能,不是特别频繁的,暴力匹配或许是个还不错的方案,因为简单易实现难出错:

敏感词过滤-AC自动机_第1张图片

是的,不要想太多,暴力匹配就好了。仔细想想,并发量不高,不会产生大量匹配过程,CPU不会太繁忙,暴力匹配也并不会对系统造成什么影响。


但如果是用在IM(及时消息)系统中呢?比如直播。

该类系统的特点是,在一段时间内,会同时有大量的请求。字符串处理是很耗CPU的操作,用上面暴力匹配方式的话,大量并发的请求,会不会造成大量的消息延时?


所以现在我们需要一种结构,可以存储多个字符串,并且快速匹配,最适合的莫过于Trie(字典树)了,也就是网上常说的DFA(确定有限状态机):

敏感词过滤-AC自动机_第2张图片

(不好意思,写的有点紧凑了,屏幕大小有限。)这种办法的好处很明显了,把词库数量忽略了,计算量只剩10*1k=1w了。


上面字典树的好处很明显,但也会发现一个问题,就是每次都从根上走一遍,那有没有办法呢?有,就是用AC自动机,在每个节点上多记录一个fail指针,但这种方法优化下来,最终的结果还是10*1k=1w。


这里先推荐一个AC自动机的库:https://github.com/eachain/aca,github上的一个AC自动机的库,这个库比较好玩的是Debug功能,可以把整棵树打印出来,方便调试及学习用。


好,言归正传,仔细观察会发现,fail指针指向的节点多数还不是完整的单词,这就造成了多数时候的跳转是无用的。所以上面的库优化了这一点,fail指针指向的就是一个完整的单词,这样就会把失配情况抛弃掉。但同样的,这也只是无限逼近O(1k)。


我把一个7700大小的库导入试了一下,发现大多情况是这样的:

敏感词过滤-AC自动机_第3张图片

发现了么?只有及其少数的fail指针是有指向的,翻几屏发现fail全是0的太常见了,当然这是优化过后的,如果是优化之前的,应该会多一些有指向的fail。


到现在为止,我们说的都是机械的扫描字符串的办法,会有很多误判的时候,比如“江阴毛纺厂”,不能做分词,就会误判了。但那些方法已经超出本篇范围,这里就不说了。


最后,如果大家要做敏感词过滤机制,而且可以有一定误判的情况下,建议用优化后的AC自动机,99%的情况下,时间复杂度等于串长。

你可能感兴趣的:(字典树)