1.DFA算法简介
DFA(Deterministic Finite Automaton) 是一种非递归自动机,也称为确定有穷自动机
。它是通过event和当前的state得到nextstate,即event+state=nextstate。
**确定**:状态以及引起状态转换的事件都是可确定的。
**有穷**:状态以及引起状态转换的事件的数量都是可穷举的。
对于以下状态转换图:
我们可以将每个文本片段作为状态,例如“匹配关键词”可拆分为“匹”、“匹配”、“匹配关”、“匹配关键”和“匹配关键词”五个文本片段。
过程:
- 初始状态为空,当触发事件“匹”时转换到状态“匹”;
- 触发事件“配”,转换到状态“匹配”;
- 依次类推,直到转换为最后一个状态“匹配关键词”。
再让我们考虑多个关键词的情况,例如“匹配算法”、“匹配关键词”以及“信息抽取”。
可以看到上图的状态图类似树形结构,也正是因为这个结构,使得 DFA 算法在关键词匹配方面要快于关键词迭代方法(for 循环)。
2.Java对DFA算法的实现思路
在Java中实现敏感词过滤的关键就是DFA算法的实现。
我们可以认为,通过S query U、V
,通过U query V、P
,通过V query U P
。通过这样的转变我们可以将状态的转换转变为使用Java集合的查找
。
如果有以下词为敏感词:日本人、日本鬼子
这样我们就将我们的敏感词库构建成了一个类似与一颗一颗的树,这样我们判断一个词是否为敏感词时就大大减少了检索的匹配范围。比如我们要判断日本人,根据第一个字我们就可以确认需要检索的是那棵树,然后再在这棵树中进行检索。
但是如何来判断一个敏感词已经结束了呢?利用标识位来判断。
所以对于这个关键是如何来构建一棵棵这样的敏感词树。下面我以Java中的HashMap为例来实现DFA算法。以日本人,日本鬼子为例,具体过程如下:
- 首先获取到根节点HashMap,判断“日”是否存在于根节点当中,如果不存在,则表示该敏感词还不存在。则以“日”为key,创建新的HashMap为value做为根节点。
- 如果存在,则表示已经存在含有以“日”开头的敏感词。设置hashMap = hashMap.get(“日”),接着依次匹配“本”、“人”。
- 判断该字是否为该词中的最后一个字。若是表示敏感词结束,设置标志位isEnd = 1,否则设置标志位isEnd = 0;
3.Java对DFA算法的实现案例
假如以“日本人”和“日本鬼子”为敏感词,以下为实现过程
代码实现:
结果如下:
4.综合实战
4.1敏感词库初始化类
==该类的作用主要是读取敏感词文件,并使用DFA
算法初始化敏感词库。==
public HashMap<String, Object> getSensitiveWordHashMap():
调用readSensitiveWordFile()
方法获取到敏感词集合,再调用initSensitiveHashMap()
方法使用DFA算法初始化敏感词库。
private Set readSensitiveWordFile()
读取敏感词文件,返回敏感词集合
private Map initSensitiveHashMap(Set strings):
将敏感词集合封装为HashMap<String,Object>
类型,初始化敏感词库。
4.2敏感词工具类
==该类的采用单例模式获取到敏感词库,并对字符串进行敏感词过滤替换。==
public void init():
@PostConstruct
注解表示该方法在对象实例化后执行,进行敏感词库初始化
。
public String replaceSensitiveWord(String string, int matchType):
替换字符串当中的敏感词。首先调用getSensitiveWord()
方法获取到当前字符串当中的敏感词集合,并遍历敏感词集合。调用getReplaceString
方法根据敏感词长度生成对应的替代词。替换当前敏感词。
private String getReplaceString(int length)
根据敏感词长度生成对应的替换字符串。
public Set getSensitiveWord(String string, int matchType):
获取到敏感词集合。在该方法中根据用户输入的字符串,调用getStringLength()
方法获取到敏感词在此字符串当中的索引位置。截取敏感词并添加到敏感词集合中返回。
public int getStringLength(String string, int beginIndex, int matchType):
返回敏感词长度。在该方法中获取到敏感词库,根据敏感词库检查文字中是否包含敏感字符,如果存在,则返回敏感词字符的长度,不存在返回0。
4.3测试
测试类测试:
查看控制台打印: