为什么要学习这个算法

我们公司一直都有的一个敏感词检测服务,前一段时间遇到了瓶颈,因为词库太多了导致会有一些速度过慢,而且一个正则表达式已经放不下了,需要进行拆分正则才可以。

正好我以前看过有关 dfa 的介绍,但是并没有深入的进行研究,所以就趁着周末好好的了解一下这个东西。跟 php 的正则进行一下对比,看看速度如何,如果表现较好,说不定还能用得上。

什么是 dfa

通过百度可以知道 dfa确定有穷自动机 的缩写。
应该还会见到类似下面图的说明

原谅我实在一些,我这人数学不好不说,貌似看图能力也不行,这个图恕我直言我没看懂。所以关于精准的解释,请大家去百度或者 google 自行查阅了。

我的理解

说明之前,我们先看看做检测需要准备的东西

  • 一个组织好的关键词树
  • 待检测的字符串

什么是组织好的关键词树

我们一批需要检测词库,比如下面这些

日本人,日本鬼子,日本人傻,破解*版

先做个解释,前三个大家都能看懂,那么 * 是什么,这个是我定义的通配符,代表着 * 可以是 0 - n 个占位符用来替代在关键词中间插入混淆字符。至于可以替换几个我们可以在代码中进行定义,需要注意 n 越大,速度就会越慢。

说明完了,来看看构造好的树是什么一样的,应该是跟下图差不多的。

为什么要手动画一个,因为需要对比,我的理解跟程序是否一致,如果不一致,就要找出程序是不是写的不对了。那么我们来看看程序生成的是啥样的。

程序生成的跟图片一致,到这里还都是正确的。

待检测的字符串

这个就很容易理解了,就是我们需要检测的字符串。

为什么要组织好那样的一棵树(算法思路)

这块需要先说一个概念

它是是通过event和当前的state得到下一个state,即event+state=nextstate

这句话,或者类似的话你会在绝大多数的解释文章里面看到。而我的理解就是,一个字符一个字符的检测,如果检测的字符在我们的树种,就进入命中的树,看下一个字在不在树里面,如果持续的命中就持续进入,最后完全命中了,也就是那个字的子树只有一个元素,并且元素的键是 end (这里是在我们的这个例子中,看图就明白了)。就是完全命中了关键词,就可以记录命中,或者准备替换了。

这里说一个可以优化的点,看我们的例子有两个词 日本人,日本鬼子 这两个,如果为了快,完全可以去掉第二个词,质保流一个就行了,这样当检测到 end 就可以直接屏蔽或者记录了,而在我们的例子中,还需要判断元素数量,不是 1 的情况下还得继续深入,看看是不是命中了长尾。

这样的长尾检测会引发一个问题,那就是 回滚,当我们命中了前置的词,后续的没有命中的时候就得记录并且回滚,这个回滚的长度是是多少呢?其实不仅仅是没有命中长尾的回滚,还有一个 回滚 操作,就是检测率几个字之后就没命中率额,就得回顾,这个回滚的长度是,已检测字符长度 - 1 的长度 。那么没有命中长尾的长度我们就知道了,已检测字符长度 - 上次命中的长度 就可以了。

下面我们来看看代码实现。

// 通配符的数量
$maskMin = 0;
$maskMax = 3;
// 关键词词典字符串,这个部分的处理自己可以替换
$dict = "傻瓜";
$checkDfaTree = [];
$dictArr = explode(',', $dict);
// 重组一下带有 * 通配符的数组
$fullDictArr = [];
foreach ($dictArr as $word) {if (mb_strpos($word, '*') !== false) {// 带有通配符就把通配符去掉for ($maskIndex = $maskMin; $maskIndex <= $maskMax; $maskIndex++) {$maskString = str_pad('', $maskIndex, '*');$inputWord = str_replace('*', $maskString, $word);$fullDictArr[] = $inputWord;}} else {$fullDictArr[] = $word;}
}foreach ($fullDictArr as $word) {// 每次开始新词都要回到树的根部$treeStart = &$checkDfaTree;$wordLen = mb_strlen($word);for ($i = 0; $i < $wordLen; $i++) {$char = mb_substr($word, $i, 1);$treeStart[$char] = isset($treeStart[$char]) ? $treeStart[$char] : [];if ($i + 1 == $wordLen) {// 如果已经是次的结尾了就设置null$treeStart[$char]['end'] = true;}// 移动指针到下一个$treeStart = &$treeStart[$char];}
}
// 遍历str
$start = microtime(true);
$checkMessageLen = mb_strlen($checkMessage);
$wordArr = [];
$checkTreeStart = &$checkDfaTree;
$hasPrefixLength = 0;
$targetWord = '';for ($i = 0; $i < $checkMessageLen; $i++) {// 获取一个字符$char = mb_substr($checkMessage, $i, 1);if (isset($checkTreeStart[$char])) {// 如果有这个字就进入子树里面if (isset($checkTreeStart[$char]['end']) && $checkTreeStart[$char]['end'] === true) {// 如果包含这个标识,就记录标识$hasPrefixLength = mb_strlen($targetWord);}$checkTreeStart = &$checkTreeStart[$char];$targetWord .= $char;} else if (isset($checkTreeStart['*'])) {// 如果有通配符就进入子树$checkTreeStart = &$checkTreeStart['*'];$targetWord .= $char;} else {if ($hasPrefixLength) {$wordArr[] = mb_substr($targetWord, 0, $hasPrefixLength + 1);// 回滚$i -= mb_strlen($targetWord) - $hasPrefixLength;} else {// 回滚$i -= mb_strlen($targetWord);}// 回到头部$checkTreeStart = &$checkDfaTree;$targetWord = '';$hasPrefixLength = 0;}if (count($checkTreeStart) == 1 && isset($checkTreeStart['end']) && $checkTreeStart['end'] === true) {// 子树只有一个并且是end 就说明是命中了// 赋值$wordArr[] = $targetWord;// 清空$targetWord = '';// 回到头部$checkTreeStart = &$checkDfaTree;$hasPrefixLength = 0;}
}
var_dump($wordArr);
echo "<br /> useTime:" . (microtime(true) - $start) * 1000;

下面这个就是匹配加测试了,目前我能想到的都测试通过了,如果有问题,可以回复我。

结论

目前来看,效率是比正则要好一些,命中的情况下速度差不多,没命中的情况下表现要优于正则。

争取能让大家都能看懂的 DFA 算法相关推荐

  1. 人人都能看懂的EM算法推导

    作者丨August@知乎(已授权) 来源丨https://zhuanlan.zhihu.com/p/36331115 编辑丨极市平台 估计有很多入门机器学习的同学在看到EM算法的时候会有种种疑惑:EM ...

  2. em算法 实例 正态分布_人人都能看懂的EM算法推导

    ↑ 点击蓝字 关注极市平台作者丨August@知乎(已授权)来源丨https://zhuanlan.zhihu.com/p/36331115编辑丨极市平台 极市导读 EM算法到底是什么,公式推导怎么去 ...

  3. 小白都能看懂最小生成树prime算法

    定义不多说,说说代码的实现. 用落谷上的题目来说明代码的正确性 落谷上的题目 邻接矩阵的实现 定义一个生成树点的集合A,和图中其他点 的集合B 先任意选择一个点a加入到A中,即visit[a]=tru ...

  4. 《小学生都能看懂的快速沃尔什变换从入门到升天教程》(FWT / FMT / FMI)(最最严谨清晰的证明!零基础也能得学会!)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 0x00 卷积 0x01 多项式 0x02 卷积的定义 0x03 卷积的基本性质 0x04 位运 ...

  5. 兄弟,用大白话给你讲小白都能看懂的分布式系统容错架构

    点击上方"蓝字", 右上角选择"设为星标" 周一至周五早11点半!精品文章准时送上! 本文来自石杉的架构笔记 目录 (1)TB级数据放在一台机器上:难啊! (2 ...

  6. 春节充电 | 文科生都能看懂的机器学习教程:梯度下降、线性回归、逻辑回归(附动图解释)...

    来源:新智元 本文约4200字,建议阅读10+分钟. 本文浅显易懂的方式讲解机器学习,力求让没有理科背景的读者都能看懂. [ 导读 ]虽然在Coursera.MIT.UC伯克利上有很多机器学习的课程, ...

  7. 文科生都能看懂的机器学习教程:梯度下降、线性回归、逻辑回归

    来源:新智元 本文约4200字,建议阅读10+分钟. 本文浅显易懂的方式讲解机器学习,力求让没有理科背景的读者都能看懂. [ 导读 ]虽然在Coursera.MIT.UC伯克利上有很多机器学习的课程, ...

  8. 人人都能看懂LSTM

    这是在看了台大李宏毅教授的深度学习视频之后的一点总结和感想.看完介绍的第一部分RNN尤其LSTM的介绍之后,整个人醍醐灌顶.本篇博客就是对视频的一些记录加上了一些个人的思考. 0. 从RNN说起 循环 ...

  9. 小学生都能看懂的FFT!!!

    小学生都能看懂的FFT!!! 前言 在创新实践中心偷偷看了一天FFT资料后,我终于看懂了一点.为了给大家提供一份简单易懂的学习资料,同时也方便自己以后复习,我决定动手写这份学习笔记. 食用指南: 本篇 ...

最新文章

  1. 终于不瞎编了!AI学会了“谷歌一下”,回答问题正确率达90% | DeepMind
  2. 垃圾回收机制和菜单栏工具栏
  3. [USACO]地震 (二分答案+最优比率生成树详解)
  4. HEVC/H265 解码图表,请对照代码看。
  5. docker管理监控方案
  6. c语言数组题库答案,C语言数组百道练习题含答案.pdf
  7. pandas基础知识---2
  8. 增霸卡出现问题 保护解不开
  9. 数字图像处理(冈萨雷斯 第三版)
  10. jquery form表单序列化,并ajax实现提交后台
  11. ffmpeg 双轨的mp3转成8k的wav 脚本
  12. 投影仪买哪个好?家用投影仪哪种好
  13. sql基础语法,非常全建议收藏(大白菜程序猿欢迎大家关注)
  14. 游戏支付接口平台如何选择
  15. 订单管理系统(OMS)搭建实战 - 低代码拖拽定制订单管理系统
  16. 手机上该怎么合并PDF?这个方法可不要错过
  17. 自然语言处理之维特比(Viterbi)算法
  18. 网络空间安全未来就业前景和就业方向,看着六点
  19. 想看微信读书终生无限卡,瞅这儿
  20. mysql替换空行解决导出到excel时由于内容有换行回车导致的问题

热门文章

  1. 如何在前台输出代码<xmp>,标签,并且是内容自动换行
  2. js中call 的使用, call方法的继承
  3. 信息技术未来发展方向
  4. GitHub 安装及使用
  5. 尖峰在线培训 http://www.jianfengedu.com/
  6. 从破解虫脑到攻克人脑:一条“永生之路”的新赛道?
  7. msysgit - Windows Git安装配置
  8. AI从感知走向智能认知
  9. Docker学习笔记 — Swarm搭建Docker集群
  10. Halcon形态学梯度