【面试被虐】说说游戏中的敏感词过滤是如何实现的?

版权声明:本文为苦逼的码农原创。未经赞成禁止任何形式转载,特别是那些复制粘贴到别的平台的,不然,一定追究。欢迎你们多多转发,谢谢。面试

小秋今天去面试了,面试官问了一个与敏感词过滤算法相关的问题,然而小秋对敏感词过滤算法一点也没据说过。因而,有了下下事情的发生.....算法

面试官开怼

面试官:玩过王者荣耀吧?了解过敏感词过滤吗?,例如在游戏里,若是咱们发送“你在干吗?麻痹演员啊你?”,因为“麻痹”是一个敏感词,因此当你把聊天发出来以后,咱们会用“**”来表明“麻痹”此次词,因此发送出来的聊天会变成这样:“你在干吗?**演员啊你?”。数组

小秋:据说过啊,在各大社区也常常看到,例如评论一个问题等,一些粗话常常被过滤掉了。数据结构

面试官:嗯,若是我给你一段文字,以及给你一些须要过滤的敏感词,你会怎么来实现这个敏感词过滤的算法呢?例如我给你一段字符串“abcdefghi",以及三个敏感词"de", "bca", "bcf"。工具

小秋:(敏感词过来算法??不就是字符串匹配吗?)我能够经过字符串匹配算法,例如在字符串”abcdefghi"在查找是否存在字串“de",若是找到了就把”de“用""代替。经过三次匹配以后,接变成这样了:“abc fghi"。.net

面试官:能够说说你采用哪一种字符串匹配算法吗?3d

小秋:最简单的方法就是采用两个for循环保留求解了,不过每次匹配的都时间复杂度为O(n*m),我能够采用 KMP 字符串匹配算法,这样时间复杂度是 O(m+n)。指针

n 表示字符串的长度,m 表示每一个敏感词的长度。视频

面试官:这是一个方法,对于敏感词过滤,你还有其余方法吗?blog

小秋:(其余方法?说实话,我也以为不是采用这种 KMP 算法来匹配的了,但是,以前也没去了解过敏感词,这下要凉)对敏感词过来以前也没了解过,暂时没想到其余方法。

trie 树

面试官:了解过 trie 树吗?

小秋:(嘿嘿,数据结构这方法,我得争气点)了解过,我还用代码实现过。

面试官:能够说说它的特色吗?

小秋:trie 树也称为字典树、单词查找树,最大的特色就是共享字符串的公共前缀来达到节省空间的目的了。例如,字符串 "abc"和"abd"构成的 trie 树以下:

trie 树的根节点不存任何数据,每整个个分支表明一个完整的字符串。像 abc 和 abd 有公共前缀 ab,因此咱们能够共享节点 ab。若是再插入 abf,则变成这样:

若是我再插入 bc,则是这样(bc 和其余三个字符串没有公共前缀)

面试官:那若是再插入 "ab" 这个字符串呢?

小秋:差点说了,每一个分支的内部可能也含有完整的字符串,因此咱们能够对于那些是某个字符串结尾的节点作一个标记,例如 abc, abd,abf 都包含了字符串 ab,因此咱们能够在节点 b 这里作一个标记。以下(我用红色做为标记):

面试官:能够说说 trie 树有哪些应用吗?

小秋:trie 最大的特色就是利用了字符串的公共前缀,像咱们有时候在百度、谷歌输入某个关键字的时候,它会给咱们列举出不少相关的信息

这种就是经过 trie 树来实现的。

小秋:(嗯? trie 又称为单词查找树,好像能够用 trie 来实现刚才的敏感词匹配?面试官平白无故提 trie 树难作别有用意?)

面试官:刚才的敏感词过滤,其实也能够采用 trie 来实现,你知道怎么实现吗?

trie 树来实现敏感词过滤

小秋:(果真,面试官真是个好人啊,直接提示了,要是还不知道怎么实现,那不真凉?)我想一想........我知道了,我能够这样来实现:

先把你给个人三个敏感词:"de", "bca", "bcf" 创建一颗 trie 树,以下:

接着咱们能够采用三个指针来遍历,我直接用上面你给你例子来演示吧。

一、首先指针 p1 指向 root,指针 p2 和 p3 指向字符串第一个字符

二、而后从字符串的 a 开始,检测有没有以 a 做为前缀的敏感词,直接判断 p1 的孩子节点中是否有 a 这个节点就能够了,显然这里没有。接着把指针 p2 和 p3 向右移动一格。

三、而后从字符串 b 开始查找,看看是否有以 b 做为前缀的字符串,p1 的孩子节点中有 b,这时,咱们把 p1 指向节点 b,p2 向右移动一格,不过,p3不动。

四、判断 p1 的孩子节点中是否存在 p2 指向的字符c,显然有。咱们把 p1 指向节点 c,p2 向右移动一格,p3不动。

五、判断 p1 的孩子节点中是否存在 p2 指向的字符d,这里没有。这意味着,不存在以字符b做为前缀的敏感词。这时咱们把p2和p3都移向字符c,p1 仍是还原到最开始指向 root。

六、和前面的步骤同样,判断有没以 c 做为前缀的字符串,显然这里没有,因此把 p2 和 p3 移到字符 d。

七、而后从字符串 d 开始查找,看看是否有以 d 做为前缀的字符串,p1 的孩子节点中有 d,这时,咱们把 p1 指向节点 b,p2 向右移动一格,不过,p3和刚才同样不动。(看到这里,我猜你已经懂了)

八、判断 p1 的孩子节点中是否存在 p2 指向的字符e,显然有。咱们把 p1 指向节点 e,而且,这里e是最后一个节点了,查找结束,因此存在敏感词de,即 p3 和 p2 这个区间指向的就是敏感词了,把 p2 和 p3 指向的区间那些字符替换成 *。而且把 p2 和 p3 移向字符 f。以下:

九、接着仍是重复一样的步骤,知道 p3 指向最后一个字符。

复杂度分析

面试官:能够说说时间复杂度吗?

小秋:若是敏感词的长度为 m,则每一个敏感词的查找时间复杂度是 O(m),字符串的长度为 n,咱们须要遍历 n 遍,因此敏感词查找这个过程的时间复杂度是 O(n * m)。若是有 t 个敏感词的话,构建 trie 树的时间复杂度是 O(t * m)。

这里我说明一下,在实际的应用中,构建 trie 树的时间复杂度我以为能够忽略,由于 trie 树咱们能够在一开始就构建了,之后能够无数次重复利用的了。而刚才的 kmp 算法时间复杂度是 t *(m+n),不过kmp须要维护 next 数组比较费空间,并且在实际状况中,敏感词的数量 t 是比较大,而 n 反而比较小的吧。

十、若是让你来 构建 trie 树,你会用什么数据结构来实现?

小秋:我通常使用 Java,我会采用 HashMap 来实现,由于一个节点的字节点个数未知,采用 HashMap 能够动态拓展,并且能够在 O(1) 复杂度内判断某个子节点是否存在。

面试官:嗯,回去等通知吧。

总结

今天主要将了 trie 树以及 trie 树的一些应用,还要就是如何经过 trie 树来实现敏感词的过滤,至于代码的实现,我这里就不给出了,在实现的时候,为了防止这种”麻 痹"或者“麻¥痹”等,咱们也要对特殊字符进行过滤等,有兴趣的能够去实现一波。

今天也是第一次尝试采用这种对话的方式来写文章,可能写的没有日常的好,不过我会慢慢改进,但愿你们多多支持。

最后推荐下个人公众号:苦逼的码农,主要分享一下技术文章、面试题、算法题,各类工具、视频资源等,里面已有100多篇原创文章,期待各路英雄来交流,点击便可扫码关注戳我便可关注

相关文章
相关标签/搜索