sensitive-word 敏感词之 DFA 算法(Trie Tree 算法)详解

拓展阅读

敏感词工具实现思路

DFA 算法讲解

敏感词库优化流程

java 如何实现开箱即用的敏感词控台服务?

各大平台连敏感词库都没有的吗?

v0.10.0-脏词分类标签初步支持

v0.11.0-敏感词新特性:忽略无意义的字符,词标签字典

v0.12.0-敏感词/脏词词标签能力进一步增强

背景

想实现一个基于敏感词库的敏感词工具。

遍历匹配

发现如果是逐个字符遍历的话,效率实在是太低。

这里我首先想到了两种算法:

KMP 算法

BF 暴力匹配算法

当然单纯只是匹配,其实性能依然非常的低。

正则表达式

当然还有一种方式就是基于正则表达式,个人感觉这种性能也比较差。

正则表达式

直接查了下资料,可以使用 DFA 算法来解决这个问题。

DFA 算法

在实现文字过滤的算法中,DFA是比较好的实现算法。

DFA 即 Deterministic Finite Automaton,也就是确定有穷自动机,它是是通过event和当前的state得到下一个state,即event+state=nextstate。

流程图示

下图展示了其状态的转换

image

在这幅图中大写字母(S、U、V、Q)都是状态,小写字母a、b为动作。

通过上图我们可以看到如下关系

            a b b
S -----> U S -----> V U -----> V

ps: 说到有穷状态机,又让我想起了正则表达式。感觉冥冥之中,这些东西还是相通的。

在实现敏感词过滤的算法中,我们必须要减少运算,而DFA在DFA算法中几乎没有什么计算,有的只是状态的转换。

开源地址

为了便于大家学习,项目开源地址如下,欢迎 fork+star 鼓励一下老马~

sensitive-word

java 实现

词库准备

敏感词库就是一行行敏感词,比如【黄色】

虽然说这个词可能是中性的,但是我们只是举一个比较和谐的例子。

流程梳理

我们根据上面的图,可以将状态图整理如下:

image

同时这里没有状态转换,没有动作,有的只是Query(查找)。

我们可以认为,通过S query U、V,通过U query V、P,通过V query U P。

通过这样的转变我们可以将状态的转换转变为使用Java集合的查找。

例子

诚然,加入在我们的敏感词库中存在如下几个敏感词:日本人、日本鬼子。

那么我需要构建成一个什么样的结构呢?

首先:

query 日 ---> {本}、query 本 --->{人、鬼子}、query 人 --->{null}、query 鬼 ---> {子}

形如下结构:

image

这样我们就将我们的敏感词库构建成了一个类似与一颗一颗的树,这样我们判断一个词是否为敏感词时就大大减少了检索的匹配范围。比如我们要判断日本人,根据第一个字我们就可以确认需要检索的是那棵树,然后再在这棵树中进行检索。

如何判断结束

但是如何来判断一个敏感词已经结束了呢?

利用标识位来判断。

所以对于这个关键是如何来构建一棵棵这样的敏感词树。

具体流程

下面我已Java中的HashMap为例来实现DFA算法。

具体过程如下:

日本人,日本鬼子为例

1、在hashMap中查询“日”看其是否在hashMap中存在,如果不存在,则证明已“日”开头的敏感词还不存在,则我们直接构建这样的一棵树。跳至3。

2、如果在hashMap中查找到了,表明存在以“日”开头的敏感词,设置hashMap = hashMap.get(“日”),跳至1,依次匹配“本”、“人”。

3、判断该字是否为该词中的最后一个字。若是表示敏感词结束,设置标志位 isEnd = 1,否则设置标志位 isEnd = 0;

image

java 程序实现

DFA 树的初始化

@SuppressWarnings("unchecked")
public void initWordMap(Collection<String> collection) {// 避免重复加载if (MapUtil.isNotEmpty(innerWordMap)) {return;}long startTime = System.currentTimeMillis();// 避免扩容带来的消耗innerWordMap = new HashMap(collection.size());for (String key : collection) {if (StringUtil.isEmpty(key)) {continue;}// 用来按照相应的格式保存敏感词库数据char[] chars = key.toCharArray();final int size = chars.length;// 每一个新词的循环,直接将结果设置为当前 map,所有变化都会体现在结果的 map 中Map currentMap = innerWordMap;for (int i = 0; i < size; i++) {// 截取敏感词当中的字,在敏感词库中字为HashMap对象的Key键值char charKey = chars[i];// 如果集合存在Object wordMap = currentMap.get(charKey);// 如果集合存在if (ObjectUtil.isNotNull(wordMap)) {// 直接将获取到的 map 当前当前 map 进行继续的操作currentMap = (Map) wordMap;} else {//不存在则,则构建一个新的map,同时将isEnd设置为0,因为他不是最后一Map<String, Boolean> newWordMap = new HashMap<>(8);newWordMap.put(AppConst.IS_END, false);// 将新的节点放入当前 map 中currentMap.put(charKey, newWordMap);// 将新节点设置为当前节点,方便下一次节点的循环。currentMap = newWordMap;}// 判断是否为最后一个,添加是否结束的标识。if (i == size - 1) {currentMap.put(AppConst.IS_END, true);}}}long endTime = System.currentTimeMillis();System.out.println("Init sensitive word map end! Cost time: " + (endTime - startTime) + "ms");
}

DFA 树的使用

/*** 检查敏感词* <p>* (1)如果未命中敏感词,直接返回 0* (2)命中敏感词,则返回敏感词的长度。** ps: 这里结果进行优化,* 1. 是否包含敏感词。* 2. 敏感词的长度* 3. 正常走过字段的长度(便于后期替换优化,避免不必要的循环重复)** @param txt           文本信息* @param beginIndex    开始下标* @param validModeEnum 验证模式* @param context 执行上下文* @return 敏感词对应的长度* @since 0.0.1*/
private int checkSensitiveWord(final String txt, final int beginIndex,final ValidModeEnum validModeEnum,final IWordContext context) {Map nowMap = innerWordMap;// 记录敏感词的长度int lengthCount = 0;int actualLength = 0;for (int i = beginIndex; i < txt.length(); i++) {char c = txt.charAt(i);char charKey = getActualChar(c, context);// 判断该字是否存在于敏感词库中// 并且将 nowMap 替换为新的 map,进入下一层的循环。nowMap = (Map) nowMap.get(charKey);if (ObjectUtil.isNotNull(nowMap)) {lengthCount++;// 判断是否是敏感词的结尾字,如果是结尾字则判断是否继续检测boolean isEnd = (boolean) nowMap.get(AppConst.IS_END);if (isEnd) {// 只在匹配到结束的时候才记录长度,避免不完全匹配导致的问题。// eg: 敏感词 敏感词xxx// 如果是 【敏感词x】也会被匹配。actualLength = lengthCount;// 这里确实需要一种验证模式,主要是为了最大匹配从而达到最佳匹配的效果。if (ValidModeEnum.FAIL_FAST.equals(validModeEnum)) {break;}}} else {// 直接跳出循环break;}}return actualLength;
}

DFA 算法的思考

虽说性能上没有任何问题。

但是 DFA 有一个缺点,那就是占用的内存和敏感词字典的大小成正比。

开源框架

参考 sensitive-word

参考资料

Java实现敏感词过滤

使用 DFA 实现文字过滤

java实现敏感词过滤(DFA算法)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/285135.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ELK(九)—logstash

目录 简介安装部署测试 配置详解编写配置文件连接elasticsearch将数据推送到elasticsearch中。 简介 Logstash 是一个开源的服务器端数据处理管道&#xff0c;由 Elastic 公司维护和开发。它被设计用于从不同来源收集、处理和转发数据&#xff0c;以供 Elasticsearch 进行存储…

Linux发行版比较:Ubuntu、CentOS、Red Hat与其他系统的优劣分析

导言 Linux作为开源操作系统&#xff0c;有众多不同的发行版&#xff0c;每个发行版都有其独特的特性和适用场景。本文将聚焦于比较Ubuntu、CentOS、Red Hat和其他系统&#xff0c;深入分析它们的优势、用途以及在不同领域的应用。Linux操作系统的生态系统中&#xff0c;Ubuntu…

cesium学习笔记(问题记录)——(三)

一、根据点跟角度计算另一点坐标&#xff08;三维球体&#xff09; export const getAnotherPoint (lon: number, lat: number, angle: number, distance: number) > {// WGS84坐标系var a 6378137; // 赤道半径var b 6356752.3142; // 短半径var f 1 / 298.257223563;…

redis:六、数据过期删除策略(惰性删除、定期删除)和基于redisson实现的分布式锁(看门狗机制、主从一致性)和面试模板

数据过期删除策略 Redis的过期删除策略&#xff1a;惰性删除 定期删除两种策略进行配合使用 惰性删除 惰性删除&#xff1a;设置该key过期时间后&#xff0c;我们不去管它&#xff0c;当需要该key时&#xff0c;我们在检查其是否过期&#xff0c;如果过期&#xff0c;我们就…

MySQL数据库,触发器、窗口函数、公用表表达式

触发器 触发器是由事件来触发某个操作&#xff08;也包含INSERT、UPDATE、DELECT事件&#xff09;&#xff0c;如果定义了触发程序&#xff0c;当数据库执行这些语句时&#xff0c;就相当于事件发生了&#xff0c;就会自动激发触发器执行相应的操作。 当对数据表中的数据执行…

Redis——多级缓存

JVM进程缓存 为了演示多级缓存&#xff0c;这里先导入一个商品管理的案例&#xff0c;其中包含商品的CRUD功能。将来会给查询商品添加多级缓存。 导入Demo数据 1.安装mysql 后期做数据同步需要用到MySQL的主从功能&#xff0c;所以需要在虚拟机中&#xff0c;利用Docker来运…

任务十六:主备备份型防火墙双机热备

目录 目的 器材 拓扑 步骤 一、基本配置 配置各路由器接口的IP地址【省略】 1、配置BGP协议实现Internet路由器之间互联 2、防火墙FW1和FW2接口IP配置与区域划分 3、配置区域间转发策略 4、配置NAPT和默认路由 5、配置VRRP组&#xff0c;并加入Active/standby VGMP管…

图片转excel:二种合并方式,有何区别?

图片怎么转为可编辑的excel&#xff0c;并且将转换结果合并为一个表&#xff1f;打开眼精星表格文字识别电脑客户端&#xff0c;我们可以看到顶部有一个功能&#xff0c;名为“表格合并”&#xff0c;而在表格识别模块提交选项里&#xff0c;我们会发现有“合并”选项&#xff…

什么是关键词排名蚂蚁SEO

关键词排名是指通过搜索引擎优化&#xff08;SEO&#xff09;技术&#xff0c;将特定的关键词与网站相关联&#xff0c;从而提高网站在搜索引擎中的排名。关键词排名对于网站的流量和用户转化率具有至关重要的影响&#xff0c;因此它是SEO工作中最核心的部分之一。 如何联系蚂…

C# WPF上位机开发(usb设备访问)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前很多嵌入式设备都支持usb访问&#xff0c;特别是很多mcu都支持高速usb访问。和232、485下个比较&#xff0c;usb的访问速度和它们基本不在一个…

微软近日推出了Phi-2,这是一款小型语言模型,但其性能却十分强大

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

redis各种数据类型的应用场景

String应用场景 单值缓存 SET key value GET key 对象缓存 SET user:1 value(json格式数据)MSET user:1:name zhuge user:1:balance 1888 MGET user:1:name user:1:balance 分布式锁 SETNX product:10001 true //返回1代表获取锁成功 …