这个问题我在曹政博客上看到过相似的,后来一搜,知乎上貌似有人给过一些思路。
==============================
曹政@
caoz 的开发工程师招聘问题三:如何实现一个快速有效的,基于自定义词典精确匹配的分词系统?修改
一个典型问题,目前政府有屏蔽词表,每个网站都要遵守,发帖的时候会自动替换屏蔽词;另一个场景是诸如新浪新闻等媒体往往有商业词表,发新闻的时候会自动建立关键词铆接。这个相当于一个简单的基于词典的分词系统,下面的问题就是,如何实现一个快速有效的,基于自定义词典精确匹配的分词系统,一是要满足每天几万篇,几十万篇文章发布的要求;另一个必须的要求是,当词库倍增扩展时(比如10万词),效率的影响不允许是线性降低的。
answer1
==============================
这个有很多办法,其实跟分词不一样,就是一个字符串匹配问题。
方法1,双哈希:
有2个哈希表,第一个是缩小范围的判定哈希,第二个是不同字数屏蔽词的哈希表。
每当我们读到一个字,就到第一个表里取一下,可以得到以这个字开头的屏蔽词的长度分别有哪些,比如(2,3,5)。然后分别从这个字开始,分2,3,5个字的词,去查第二个哈希表,查到了则返回危险,否则继续判定下一个字。
复杂度:O(L*n),L为以每个字开头的词长度的平均个数,n为输入流长度。(真绕)
方法2,有限自动机:
有限自动机说白了就是手工展开的正则表达式,把词表综合成一个巨大的有限自动机,每输入一个字就到自动机里查表,跳转状态,到匹配状态则为危险句子,到结束符则不危险。
时间复杂度:O(n)
空间复杂度:不可估计,可能会很大
方法3,一些其它字符串匹配算法的变形:
具体没细想,类似kmp,rk,bm的变形或许也能解决这个问题。
总之,最笨的方法是前向最大匹配,复杂度O(m*max(L)),其中max(L)为最长屏蔽词的长度。
一个好的匹配算法可以减少L的长度,检测并跳过没必要的计算。
answer2
==============================
由于敏感词范围有限,可以按字节将所有词分成N段。每字节共256种可能,维护一棵trie树,节点为256个指针。每个指针标识一字节,(比如第3个指针表示0x03这个字符)。所以一个M个字节的词被切成M份,为trie树中从根节点到第M层的一条链路。将所有待过滤词全部输入后,就能形成一棵查询trie树。其中敏感词对应的路径为通的,并且最后一个是页节点(比如abcde是敏感词,那么第5层的e对应的指针就是空的)。如果非敏感词,则无此通路或者有此通路但还有后续节点。
比如abcde是敏感词,abcdf不是
abcdf***是一个待过滤的字符串,在前四个字节匹配后,在第四层的f对应那个指针就为空。匹配失败
abc对应的c节点还有后续节点,那么abc也就不在敏感词中
在查询时,通过将文章按字节在trie树中进行查找,如果能一直有路径到叶节点,那么目前这条从根节点到叶节点的路径就是第三词。
如果不是,很明显我们需要将文章向后移一位再做对比。这样其实复杂度非常高。
解决这个问题可以引入KMP算法并将其扩展,将每个节点在匹配失败后,文章的下一字节应该到哪个节点重新开始匹配计算出来,将文章下一字节直接与这个节点进行匹配,这样每次文章只需要遍历一次。复杂度为O(n),n为文章长度。
至于空间复杂度,最坏的情况下,当所有敏感词链路没有交集时,由于使用trie树结构,整个数据会膨胀(256+256)*4 = 2048 倍,两个256分别表示一个节点中的两套指针(子节点指针与KMP需要的指针),4表示一个字节变成了4个字节(如果是32位机器的话)。 当然,如果将所有节点在一个数组下分配的话,就不需要存指针了,存数组位置即可。数据小数组可以2^16长,这时就变成2字节了,数据大2^32个节点差不多也够了吧。而且在64位机下也一样。
所以10w级别的词,按每个词平均10字节算,一共1M,最坏情况下需要2G内存实现。
http://www.zhihu.com/question/19918081