DFA 算法

为什么要学习这个算法

  前一段时间遇到了瓶颈,因为词库太多了导致会有一些速度过慢,而且一个正则表达式已经放不下了,需要进行拆分正则才可以。

正好我以前看过有关 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;

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

结论

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

EM算法求解高斯混合模型参数公式推导

高斯混合模型介绍 高斯混合模型&#xff08;Gaussian Mixture Model&#xff0c;简称GMM&#xff09;是一种经典的概率模型&#xff0c;被广泛应用于数据挖掘、模式识别和机器学习等领域。它采用多个高斯分布组合来对数据进行建模&#xff0c;每个高斯分布对应于数据中的一个子…

【Unity2D:C#Script】实现角色射击功能

一、创建子弹预制体 1. 创建子弹预制体 2. 调整图片大小、层级 二、为子弹添加碰撞体积 1. 添加Box Collider 2D、Rigidbody 2D组件 2. 锁定z轴 三、编辑敌人脚本 注&#xff1a;在以下代码中&#xff0c;只显示本章节新增的代码&#xff0c;省略原有的代码 1. 为敌人添加生…

智能无网远控再升级 向日葵Q2Pro升级版发布

无网或者内网设备也想要进行远程控制&#xff0c;是不是听上去有些天方夜谭了&#xff1f;其实这类特种设备的远程控制需求是非常强的&#xff0c;比如医疗/工控设备的远程运维、使用指导教学等等。 实际上&#xff0c;只要这类设备有屏幕&#xff0c;支持可视化的桌面操作&am…

Linux - 整理工作中常用的 Linux 命令(目录、文件、系统、进程、网络)持续更新~

目录 一、Linux 目录结构 二、Linux 中的常用指令 2.1、目录命令 cd 切换目录 pwd 打印当前所在目录 ls 展示当前目录内容 mkdir 创建目录 du 统计每个目录下的文件字节数 2.2、文件命令 which 查找 命令字 所在位置 find 查找文件 touch 创建一个空文件 cp 复制文…

签发免费https证书的方式

目录 http访问和https访问的区别 实现https后有哪些好处&#xff1a; 如何申请、安装部署免费https证书&#xff1a; 在浏览网页时&#xff0c;最常见的是http访问&#xff0c;但是也有一部分网站前缀是https&#xff0c;且浏览器网址栏会出现“安全”字样&#xff0c;或是绿…

第14章 数据分析案例——2012联邦选举委员会数据库

美国联邦选举委员会发布了有关政治竞选赞助方面的数据。其中包括赞助者的姓名、职业、雇主、地址以及出资额等信息。我们对2012年美国总统大选的数据集比较感兴趣。&#xff08;http://www.fec.gov/disclosurep/PDownload.do&#xff09;。我在2012年6月下载的数据集是一个150M…

华为设备WLAN配置之AP上线

WLAN基础配置之AP上线 配置WLAN无线网络的第一阶段&#xff0c;AP上线技术&#xff1a; 实验目标&#xff1a;使得AP能够获得来自AC的DHCP地址服务的地址&#xff0c;且是该网段地址池中的IP。 实验步骤&#xff1a; 1.把AC当作三层交换机配置虚拟网关 sys Enter system view,…

【Qt 学习笔记】Qt窗口 | 状态栏 | QStatusBar的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 状态栏 | QStatusBar的使用及说明 文章编号&#xff1a;Qt 学…

一文搞定cuda版本、显卡驱动及多CUDA版本管理

安装cuda是每个AI从业人员必经之路。网上关于cuda、显卡驱动已经相关命令很多都解释不清楚&#xff0c;于是本文梳理一下&#xff0c;既方便自己记忆&#xff0c;也方便小白学习。 CUDA 首先&#xff0c;CUDA版本&#xff0c;一般指cuda-toolkit&#xff0c;即cuda开发工具包…

开源绘图工具Rnote使用体验分享

软件介绍 Rnote,这款致力于提供矢量绘图、手写笔记以及文档注释功能的免费开源软件,逐渐成为了学生、教师以及绘图板用户的新宠。其独特之处在于,它不仅支持PDF和图片的导入导出,还拥有无限画布和适应各种屏幕大小的界面设计,这些功能使得Rnote在众多同类软件中脱颖而出。…

python抽取pdf中的参考文献

想将一份 pdf 论文中的所有参考文献都提取出来&#xff0c;去掉不必要的换行&#xff0c;放入一个 text 文件&#xff0c;方便复制。其引用是 ieee 格式的&#xff0c;形如&#xff1a; 想要只在引用序号&#xff08;如 [3]&#xff09;前换行&#xff0c;其它换行都去掉&…

【中霖教育口碑】什么情况下不允许参加注册会计师考试?

对于某些特殊情况&#xff0c;存在明确的禁止性规定&#xff0c;是不能参加注册会计师考试的&#xff0c;中霖为大家分享一下!关于注册会计师全国统一考试的资格条件&#xff0c;需明确以下要点&#xff1a; 1. 针对在前期注册会计师统一考试中因违反规定而受到禁止参加考试的…

awesome-ai4s 现已开源!超全 AI for Science 学术论文与数据资源汇总,持续更新ing

2018 年中国科学院院士鄂维南提出「AI for Science」概念&#xff0c;强调利用 AI 学习科学原理、创造科学模型来解决实际问题。同年&#xff0c;AlphaFold 崭露头角&#xff0c;从 43 种蛋白质中准确预测出了 25 种蛋白质结构。2021 年&#xff0c;AlphaFold 2 开源并预测了 9…

现代前端工程化实践:Git、Husky、Commitlint与PNPM的协同作战

引言 Git Husky 与 Commitlint 是两个在 Git 工作流程中非常实用的工具&#xff0c;它们可以帮助团队维护代码质量和提交规范。Husky 是一个 Git 钩子管理器&#xff0c;允许你在仓库级别方便地配置钩子脚本&#xff1b;而 Commitlint 则是用来规范 Git 提交信息的工具&#x…

上位机图像处理和嵌入式模块部署(mcu之芯片选择)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前市面上的mcu很多&#xff0c;有国产的&#xff0c;有进口的&#xff0c;总之种类很多。以stm32为例&#xff0c;这里面又包括了stm32f1、stm32…

AWS EC2 连接 AWS RDS(Mysql)

1 创建RDS数据库 点击创建数据库 引擎选项 模板 设置 连接 2 EC2连接Mysql $ sudo yum list mariadb* Installed Packages mariadb-connector-c.x86_64 3.1.13-1.amzn2023.0.3 amazonl…

飞睿智能超宽带UWB标签模组,简化设备开发流程,实时高速率数传交互应用

在科技飞速发展的今天&#xff0c;UWB超宽带技术因其高精度、低功耗和高安全性的特点&#xff0c;正逐渐成为智能设备定位和数据传输的新宠。 UWB技术是一种无线通信技术&#xff0c;它通过使用非常宽的频带进行数据传输&#xff0c;从而实现高数据传输速率和高精度定位。 飞…

远动通讯屏的原理和应用

远动通讯屏的原理和应用 远动通讯屏&#xff0c;是一种集显示和远程控制于一体的智能化控制设备。它可以通过网络、通信线路等方式实现与远程设备的通讯和交互&#xff0c;从而实现远程监控和控制。 远动通讯屏实现远程控制的核心原理是基于PLC&#xff08;Programmable Logic …

彩色进度条(C语言版本)

.h文件 #include<stdio.h> #include<windows.h>#define NUM 101 #define LOAD_UP 50 #define LOAD_DOWN 60 #define SLEEP_SLOW 300 #define SLEEP_FAST 70 版本1&#xff1a;&#xff08;初始版&#xff09; //v1 #include "progress.h" int main() …

C# GetManifestResourceStream 获取项目资源为null解决方案(亲测)

GetManifestResourceStream 获取项目资源为null 使用Stream s assembly.GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name resourceName) 获取资源文件&#xff0c;返回流为null&#xff0c;如图所示&#xff1a; 解决方案 设置资源文件的 属性&…