首页 > 编程语言 >【Java面试场景题】如何设计一个敏感词过滤系统?

【Java面试场景题】如何设计一个敏感词过滤系统?

时间:2024-07-02 17:57:13浏览次数:25  
标签:AC Java 敏感 面试 算法 过滤 匹配 自动机 节点

一、问题解析

下图是一个完整的文本审核流程,包括名单匹配、敏感词匹配、AI机器审核、人工审核四个环节。待审核文本需要顺次通过名单匹配、敏感词匹配、AI机器审核三个流程,若结果为嫌疑则需要人工审核,否则将直接给出确定的结果。

敏感词匹配功能可以迅速地匹配文本中的敏感词汇,算法平均耗时为50ms,因其简单、快速、直接、灵活的特点,成为了审核人员对抗垃圾文本的利器。然而身处信息爆炸时代的网民们非常“优秀”,他们源源不断的发明各种新词、谐音词来绕过敏感词检测。

例如某些用户会使用“啋票”、“采漂”等词汇来规避敏感词“彩票”,其中“啋票”不仅是谐音词,还包含多音字,常规的模式匹配算法很难保证完全命中。这不仅给运营规则带来挑战,也对匹配算法的精准度、漏杀率提出了要求。

本文将从算法选型入手,结合两个实际场景来介绍谛听系统的敏感词匹配算法。其中第一节介绍了底层算法选型思路,第二节介绍了两种实际场景下提升敏感词精准度、降低漏杀率的方案。

36.1 算法选型

敏感词匹配功能依托于模式匹配算法。模式匹配的定义是,给定一个子串,在某个字符串中找出与该子串相同的所有子串。其中给定的子串被称为模式串,被匹配的字符串被称为目标串。基于多个模式串进行匹配的算法被称为多模式匹配算法,目前成熟的多模式匹配算法有AC自动机和WM。

多模式匹配算法对比

AC自动机

WM

优势

匹配效率高模式串对匹配效率无影响

匹配效率高内存占用量少模式串预处理流程简单,速度快

劣势

内存占用量大模式串加载流程复杂,耗时较长

对模式串长度有要求(长度基本相同,有最小长度限制)模式串前缀存在大量重复时,算法性能显著下降需要根据实际情况调整算法参数

谛听的敏感词匹配业务有如下特点:

  • 词库量大,需要维护和加载百万级别的词库(模式串);
  • 敏感词与业务特性、国家政策相关性强,无法统一约定长度、前缀等特征。

WM算法对模式串的长度和前缀存在一定的要求,可能会影响业务的使用。虽然AC自动机加载耗时长,内存占用大,但敏感词加载并不频繁,且服务器内存资源充足,所以我们最终选用AC自动机作为底层算法。

36.1.1 AC自动机算法介绍

AC自动机算法(Aho–Corasick算法)是一种字符串搜索算法,可以同时将目标串与所有模式串进行匹配,算法均摊情况下具有近似于线性的时间复杂度。AC自动机的匹配原理有两个核心概念:Trie字典树、Fail指针。下面以模式串集合{“she”, “he”, “shers”, “his”, “era”}为例,来介绍这两个概念。

AC自动机算法启动时,需要将所有的模式串加载到内存中,构建成一个Trie字典树。例如下图是以模式串集合{“she”, “he”, “shers”, “his”, “era”}构建的字典树,树中每个节点代表一个字符,从根节点到某一个节点的路径,即可表示一个模式串,且红色节点表示一个字符串的终结,例如图中最右边子树上的三个节点,可代表模式串“era”。

从字典树的根节点出发,可以快速的查找到某个模式串。此外,拥有相同前缀的模式串会合并到同一个子树中,例如中间子树表示模式串“he”、 “his”,这两个字符串分别是“h”节点的一个分支。AC自动机在搜索这类字符串时,可以节省匹配的次数。

AC自动机在Trie树的基础上,为每个节点加入了Fail指针,上图使用虚线画出了部分节点的Fail指针,未画出虚线的节点,其Fail指针指向根节点。算法在某个节点匹配失败时,可以通过该指针转移到其他包含相同前缀的分支上继续匹配。

例如匹配目标串“shis”时,对于前两个字符“sh”,Trie字典树匹配到左边字数的“h”节点上,由于该节点的子节点是字符“e”,与目标串的下一个字符“i”不匹配,因此算法通过Fail指针转移到中间子树的“h”节点上继续匹配,最终命中字符串“his”。

上述的Trie字典树与Fail指针组成了AC自动机的数据模型。AC自动机匹配目标串时,会按顺序从目标串中取出字符,从Trie字典树的根节点出发,在子结点中寻找与该字符匹配的结点,若能找到,则转移到该节点,若找不到,则转移到Fail指针指向的节点。当状态转移到图中的红色节点时,就是命中了一个模式串。下图展示了AC自动机对目标串“merashisnx”进行匹配的过程。

36.1.2 谛听系统实践

谛听系统基于AC自动机算法构建了一套敏感词匹配服务,将敏感词作为模式串,文本内容作为目标串,可以实现常用的中、英文敏感词匹配。但是实际的业务有很多细分的场景,普通的AC自动机算法已不能满足业务使用需求,因此我们探索了组合敏感词匹配和拼音敏感词匹配两种匹配方式,下面分别介绍。

36.2 组合敏感词

常规的敏感词匹配算法通常匹配单个词或者短句,但某些词单独出现时并不违规,只有在与几个特定的词同时出现时,才能判定为违规。例如“澳门”、“博彩”、“网站”,这几个词,单独出现都是没问题的,但是在这句话中:“欢迎登录澳门XX博彩官方网站”,就是违规的赌博广告。针对这样的场景,我们实现了“组合敏感词”匹配方案,运营人员可以将这几个词配置成一个组合“澳门+博彩+网站”,只有这三个词同时出现时,敏感词服务才会判定这个组合命中。

组合敏感词的匹配算法依然基于AC自动机算法。由于AC自动机只能判断单个词的命中情况,因此我们将组合敏感词分割成单个敏感词,并维护各敏感词与组合间的映射关系,在AC自动机算法运行结束后,只有某个组合对应的敏感词全部命中时,才能判断该组合敏感词命中。为此我们需要给AC自动机添加一些前置和后置的处理步骤,具体步骤如下:

  1. 将组合敏感词分割为单个敏感词,并记录敏感词与组合的映射关系;
  2. 将分割后的组合敏感词添加到AC自动机的Tire树中;
  3. 运行AC自动机,匹配文本;
  4. 遍历匹配结果,将匹配的结果根据映射关系映射到相应的组合上;
  5. 记录组合的命中情况,得到最终匹配结果。

假设目前存在组合敏感词“澳门+博彩+网站”、“博彩+广告”、“华人圈+赌博”、“赌博+广告”,以及普通敏感词“暴政”,则在步骤1中,我们将组合敏感词分割为单个敏感词:“澳门”、“网站”、“博彩”、“广告”、“赌博”、“华人圈”、“暴政”,并建立图中所示的映射关系。

将这些词添加到AC自动机后,对文本“欢迎登录澳门XX博彩官方网站”进行匹配时,会命中单个敏感词“澳门”、“网站”、“博彩”。在步骤4中,算法将匹配的词映射到组合中,并标记对应的词命中。例如根据“博彩”,可取到组合“澳门+博彩+网站”、“博彩+广告”,则分别标记这两个组合中的“博彩”已命中。步骤5会根据各组合中单词的命中情况,来判断该组合是否命中,由于在步骤4中组合敏感词“澳门+博彩+网站”的三个词均被标记为命中,因此可判断该组合命中。

36.3 拼音敏感词

在评论、弹幕等创作自由度很高的场景中,某些用户为了规避机器审核,会使用一些多音字、同音字来表达敏感词汇,例如用“啋票”来代表“彩票”等。由于多音字、同音字变化较多,将一个词的所有谐音词都穷举出来是很困难的。

因此我们实现了拼音敏感词的匹配方案,将中文文本转换为拼音再匹配,通过读音匹配敏感词,即可保证命中所有的同音字,运营直接配置敏感词的拼音,例如“CAI PIAO”,即可命中“啋票”、“彩票”、“采漂”等词汇。

1、匹配流程

常规的AC自动机算法是逐字符匹配的,因此Trie树上每个节点存储一个字符,但拼音敏感词需要按照音节匹配,因此我们将Trie树的节点数据类型由char改为String,示例:

拼音敏感词的匹配关键在于将汉字准确的转换为拼音,这一点在多音字的场景下尤为重要。由于多音字的读音是受语境影响的,现有的技术条件很难确保能将多音字准确的转换为拼音,而上文提到同音词“啋票”是用户自行造的词,算法无法准确的识别语境,可能转换得到“CAI PIAO”、“XIAO PIAO”两种不同的结果。如果拼音转换不精准,则拼音敏感词也无法准确命中。

因此我们不依赖算法识别多音字的读音,而是将文本内容的所有读音都列出来匹配一遍,就可以避免避免拼音转换不精准的问题。

下图展示了文本内容与拼音的对应关系,由于存在多音字,因此存储拼音的数组从一维扩展到了二维,更像是“图”的数据结构,下文将其称为拼音图。拼音图的起始节点和终止节点之间存在多条路径,这些路径对应了多音字的所有排列组合情况,为了避免漏杀,我们需要使用AC自动机将这些路径都匹配一遍。

从第二节的匹配流程可以看出,目标串是一维数组,因此AC自动机在匹配文本时,通常采用顺序遍历的方式。而在拼音敏感词中,由于目标串采用二维数组存储,是一种类似于“图”的数据结构,不再适合使用顺序遍历的方式,因此需要采用图的遍历算法。

图的遍历算法中,最常用的就是深度优先遍历(DFS)和广度优先遍历(BFS)。由于词语是前后关联的,为了使算法更符合人类思考习惯,我们选择了DFS。

DFS算法使用栈存储节点信息,在当前分支遍历完成后,通过栈中的信息回溯到上一个分支处继续遍历。由于Trie树的状态位与拼音图的节点是相关的,在DFS回溯时,Trie树也需要同步回溯,因此需要将Trie树状态位与拼音图的节点信息一起保存到DFS栈中。下图展示了拼音敏感词的匹配流程。

2、终止条件与剪枝策略

DFS的终止条件是当所有节点都被遍历过,且算法会确保每个节点只会被遍历一次。但是DFS遍历时会在分支处回溯,所以往往终止的节点并不是待匹配文本的终点,很有可能AC自动机的匹配并未完成。

例如在下图所示的匹配流程中,左图是基于待匹配文本“朱朝阳和朋友”构建的拼音图,右图是基于拼音敏感词“PENG YOU”、“ZHAO YANG”、“NI MA”、“MA DE”构建的Trie树。左图的拼音图采用DFS算法遍历,算法最后访问的节点是蓝色节点“ZHAO”,此时拼音图中所有节点均被遍历了一次,已经达到了DFS的终止条件。

但右图中Trie树上的状态位处于蓝色节点“ZHAO”的位置,并没有达到终止状态,若此时停止匹配,则会导致无法命中敏感词“ZHAO YANG”,故算法应继续运行,直到AC自动机匹配失败为止。

因此合适的终止条件是:拼音图所有节点均被遍历 且 AC自动机匹配失败。

由于算法需要结合DFS和AC自动机的状态来判断终止条件,因此会出现拼音图中一个节点和路径被遍历多次的情况,当待匹配文本中多音字数量增多时,DFS遍历的路径数量会以笛卡尔积的形式增加。而这些路径中会存在一部分重复的情况,因此在遍历的过程中需要采取合适的剪枝策略,避免搜索一些重复的路径。

例如下面左图所示的遍历情况,路径②上“PENG”、“ YOU”两个节点已被路径①遍历过,且对应的AC自动机状态位(参考右图)前缀并不包含当前遍历节点“HU”,所以“PENG”对应的敏感词与路径②无关,不必再搜索一次。我们可以针对这种场景设计了剪枝策略,需要剪枝的路径需要满足两个条件:

  1. 首先当前节点的下一节点已被遍历过;
  2. 下一节点对应的AC自动机状态与当前节点无关。

我们为拼音图中的每个节点标记一个分支路径长度B,表示当前节点与上一个最近的分支节点间的路径长度,例如节点“PENG”对应的分支路径长度为B = 2(从“HU”到“PENG”),节点“YOU”的分支路径长度为B = 3(“HU”、“PENG”、“YOU”)。

在AC自动机的状态机中,记录Trie树上每个节点的深度D。例如状态机中“PENG”节点的深度D=1,“YOU”节点的深度D=2。从Trie树根节点到某一个节点的路径上经过的字符连接起来,为该节点对应的模式串,因此节点的深度D即为模式串的长度。当D < B时,表明当前正在匹配的模式串长度短于拼音图中当前节点的分支路径长度,所以当前的模式串与当前的路径无关。

总结一下,剪枝所需的条件为:

  1. 拼音图中下一节点已被遍历;
  2. 拼音图的分支路径长度B > Trie树节点的深度D。

二、粉丝福利 

我根据我从小白到架构师多年的学习经验整理出来了一份50W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴斯我“159”,或者评论区扣“求分享”

标签:AC,Java,敏感,面试,算法,过滤,匹配,自动机,节点
From: https://blog.csdn.net/JAVA_aik/article/details/140132750

相关文章

  • Java_多线程:实现多线程
    Java中实现多线程的常用方式:继承Thread类实现Runnable接口实现Callable接口(JDK>=1.5)线程池方式创建实现Runnable接口与Callable接口的区别实现Runnable接口和Callable接口的方式基本相同,不过Callable接口里定义的方法返回值,可以声明抛出异常。Runnable和Callable与......
  • 大学生HTML期末大作业——HTML+CSS+JavaScript运动网站(田径)
    HTML+CSS+JS【运动网站】网页设计期末课程大作业web前端开发技术web课程设计网页规划与设计......
  • 大学生HTML期末大作业——HTML+CSS+JavaScript购物商城(Oppo)
    HTML+CSS+JS【购物商城】网页设计期末课程大作业web前端开发技术web课程设计网页规划与设计......
  • java多线程-锁的介绍
    多线程中常用锁一、锁的概念二、锁的类型2.1互斥锁(也称排它锁)2.1.1Synchronized和Lock2.1.2ReentrantLock(可重入锁)2.1.3公平锁2.1.4非公平锁2.1.5中断锁2.2共享锁2.3读写锁三、悲观锁和乐观锁3.1悲观锁3.2乐观锁3.3CAS算法四、锁竞争一、锁的概念在多......
  • (Java)知其然且知其所以然系列4
    写在开头本系列内容主要涵盖我在深入学习Java过程中对一些知识点的深入理解和巩固。如果内容表达不准确或存在谬误,欢迎在评论区或私信中进行补充或指正~目录Java接口、内部类、包        接口可以继承吗?        接口继承要重写父接口的方法吗?     ......
  • 书城在线系统:基于Java和SSM框架的高效信息管理平台
    开头语:你好呀,我是计算机学长猫哥!如果有相关需求,文末可以找到我的联系方式。开发语言:Java数据库:MySQL技术:SSM框架(Spring,SpringMVC,Mybatis)工具:MyEclipse,Tomcat,MySQL系统展示首页管理员功能模块用户功能模块前台首页功能模块摘要雅博书城在线系统,一......
  • 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-1-环境准备与搭建
    1.简介Python+Playwright系列的文章还没有结束,就有好的小伙伴或者童鞋们私信公众号留言,问宏哥什么时候出Java语言的Playwright的自动化测试文章。本来想趁热打铁将Python+Playwright完结后,就开始Java语言的Playwright的自动化测试文章,但是好多人私信留言,索性就两个系列的文章......
  • Java实现登录验证 -- JWT令牌实现
    目录1.实现登录验证的引出原因2.JWT令牌2.1使用JWT令牌时2.2令牌的组成三.JWT令牌(token)生成和校验3.1引入JWT令牌的依赖3.2使用Jar包中提供的API来实现JWT令牌的生成和校验3.3使用JWT令牌验证登录1.实现登录验证的引出传统思路下:登录页面把用户名和密码交......
  • JAVA,认识类
    一、类?什么是类?官方文档解释:类(Class)是面向对象程序设计(OOP,Object-OrientedProgramming)实现信息封装的基础。类是一种用户定义的引用数据类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。拥有共同属性抽象的集合称之为类白话:简单理......
  • 数据库我是这样写出来的,Java版本1,持续更新
    了解数据库的内部原理其实很不容易,大部分的读写都停留在理论文章上,因此肖哥带着大家使用Java手写一个完整的数据库,让大家了解数据库的解析器、性能分析器、认证、查询优化器,执行引擎、存储引擎、事务管理、MVCC,数据恢复等一系列功能。这个工作量比较大,属于每日1-2更新,大家如......