1、定义
有穷自动机FA(Finite Automaton)的每一步操作都是确定的,因此可称为确定型有穷自动机。确定有穷自动机DFA(Deterministic Finite Automaton)就是说当一个状态面对一个输入符号的时候,它所转换到的是一个唯一确定的状态。
2、过程
无意中发现这个算法,发现这个DFA可以很好的解决在限定字段发送,对不规定的词语进行限定的查找,极大的提高效率。
有一段文字,
你好,我是秦始皇的后裔,我有一千多亿的资产被政府冻结了,现在需要诈骗你 100 块钱,用于解冻我们的资产,解冻完成后再骗你一百。
以前我们对一段文字中匹配不规范的词语,往往是采用将敏感词进行一个个的匹配,假设有一个敏感词-诈骗,我们以诈骗为核心,对文字进行匹配,如果存在,则返回错误。
采用DFA算法后,我们以文字为核心,对文字进行拆分,将敏感词运用DFA保存为一个有穷自动机,把拆分的文字进行匹配,极大的提升效率。
List<String> dirtywords = new ArrayList<>();
for (String s : dirty) {
boolean contains = msg.contains(s);
if (contains) {
dirtywords.add(s);
}
}
long end = System.currentTimeMillis();
System.err.println(end - start);
System.err.println(dirtywords);
运行结果:2882198个数据
花费时间为298毫秒
//进行DFA算法查询
SensitivewordFilter sensitivewordFilter = new SensitivewordFilter();
sensitivewordFilter.setDirtyWordLocalCache(dirtyWordLocalCache);
System.err.println("华丽的分割线------------------------------------");
start = System.currentTimeMillis();
Set<String> sensitiveWord = sensitivewordFilter.getSensitiveWord(msg);
System.err.println(sensitiveWord);
end = System.currentTimeMillis();
System.err.println(end - start);
同样的数据,花费为0毫秒,相差巨大
三、下面为DFA算法代码,开箱即用
类
@Component
public class SensitivewordFilter {
/**
* 获取文字中的敏感词
*
* @param txt 文字
* @return Set
*/
public Set<String> getSensitiveWord(String txt) {
return getSensitiveWordSets(txt);
}
/**
* 替换敏感字字符
*
* @param txt 文本
* @param replaceChar 替换字符,默认*
* @return String
*/
public String replaceSensitiveWord(String txt, String replaceChar) {
String resultTxt = txt;
// 获取所有的敏感词
Set<String> sets = getSensitiveWord(txt);
for (String str : sets) {
String replaceString = getReplaceChars(replaceChar, str.length());
resultTxt = resultTxt.replaceAll(str, replaceString);
}
return resultTxt;
}
/**
* 获取替换字符串
*
* @param replaceChar 替换符
* @param length 长度
* @return String
*/
private String getReplaceChars(String replaceChar, int length) {
StringBuilder resultReplace = new StringBuilder(replaceChar);
for (int i = 1; i < length; i++) {
resultReplace.append(replaceChar);
}
return resultReplace.toString();
}
/**
* 检查文字中是否包含敏感字符,检查规则如下
* @param txt 文本
* @return int 如果存在,则返回敏感词字符的长度,不存在返回0
*/
public int checkSensitiveWord(String txt) {
Set<String> sets = getSensitiveWordSets(txt);
return sets.size();
}
private Set<String> getSensitiveWordSets(String txt) {
Set<String> sensitiveWordSets = new HashSet<>();
for (int n = 0; n < txt.length(); n++) {
// 判断是否包含敏感字符
int length = judgeSensitiveWithIndex(txt, n);
if (length > 0) {
// 存在,加入list中
sensitiveWordSets.add(txt.substring(n, n + length));
// 减1的原因,是因为for会自增
n = n + length - 1;
}
}
return sensitiveWordSets;
}
/**
* 根据指定位置是否是敏感词的开始
*
* @param txt 文本
* @param beginIndex 开始位置
* @return int
*/
private int judgeSensitiveWithIndex(String txt, int beginIndex) {
// 匹配标识数默认为0
int matchFlag = 0;
char word;
Map nowMap = dirtyWordLocalCache.getSensitiveWordMap();
for (int i = beginIndex; i < txt.length(); i++) {
word = txt.charAt(i);
// 获取指定key
nowMap = (Map) nowMap.get(word);
// 存在,则判断是否为最后一个
if (nowMap != null) {
// 找到相应key,匹配标识+1
matchFlag++;
if ("1".equals(nowMap.get("isEnd"))) {
// 如果为最后一个匹配规则,结束循环,返回匹配标识数
break;
}
} else {
// 不存在,直接返回
break;
}
}
// 长度必须大于等于1,为词
if (matchFlag < 2) {
matchFlag = 0;
}
return matchFlag;
}
}