文章目录

  • 一、前言
  • 二、何为DFA算法
  • 三、DFA算法优化关键词过滤
  • 四、java代码实现
  • 五、总结

一、前言

有一个关键词库,此时需要检索一段文本中是否出现词库中的关键词,该如何实现?

小白回答:将所有的关键词放入一个set集合,然后遍历集合一个个匹配那段文本,存在的关键词放入另一个集合。实现代码如下:

public class Test131 {public static void main(String[] args) {Set<String> wordSet = new HashSet<String>();wordSet.add("产品经理");wordSet.add("产品总监");wordSet.add("程序员");String content= "产品经理工作内容包含需求收集,需求分析,需求落地,项目跟踪,项目上线,数据跟踪以及对业务人员进行培训,协助运营、销售、客服等开展工作。";Set<String> haveWordSet = new HashSet<String>();for (String word : wordSet) {if (content.contains(word)) {haveWordSet.add(word);}}System.out.println(haveWordSet);}
}
// [产品经理]

思路很简单,也很容易理解,但是当关键词有几千、几万个时,性能会急剧下降。分析一下时间复杂度,遍历关键词是O(n),从文字段落检索的时间复杂度也是O(n),合起来就是O(n^2)。

有没有其他更优的解决方案呢?有!就是DFA算法!

二、何为DFA算法

DFA即Deterministic Finite Automaton,翻译过来就是确定性有限自动机。简单原理就是:在一个有限的集合,其中的元素都有两种状态,结束和继续(可以用0代表继续,1代表结束),可以从一个元素检索到下一个元素,直到元素的状态为结束为止。

三、DFA算法优化关键词过滤

套用DFA算法的原理,例如有一些关键词:产品经理、产品总监、程序员,构建DFA算法容器如下:

看着像一棵棵树,现在判断一个词是否在词库中,比如测试员,检索是否有”测“字开头的“树”,很快就能判断没有测试员这个词,比如检索“产品经理”,则可以找到”产“字开头的“树”,直接排除了“程序员”那棵“树”,这就大大缩小了范围。

算一下时间复杂度,从词库中匹配关键词,时间复杂度是O(1),遍历文字段落依然和文字的个数有关,时间复杂度最差(如果文本没有一个关键词)为O(n),合起来就是小于等于O(n)。使用DFA实现关键词过滤的优化点在于:检索的时间复杂度不会因为关键词数量的增加而受影响,只与被检索的文字长度有关。

四、java代码实现

分析数据结构,每一个字符都要存一个状态,可以用map实现,如果状态未结束还要存下一个字符的指针,也可以用map实现,这样就是map套map。如下:一个大map里有两个key,程和产,value值又是map套map。

{程={isEnd=0, 序={isEnd=0,员={isEnd=1}}}, 产={isEnd=0,品={isEnd=0,总={isEnd=0, 监={isEnd=1}}, 经={isEnd=0, 理={isEnd=1}}}}
}

完整代码示例:

public class Test131 {public static void main(String[] args) {String content= "产品经理工作内容包含需求收集,需求分析,需求落地,项目跟踪,项目上线,数据跟踪以及对业务人员进行培训,协助运营、销售、客服等开展工作。";Set<String> wordSet = new HashSet<String>();wordSet.add("产品经理");wordSet.add("产品总监");wordSet.add("程序员");init(wordSet);System.out.println("wordMap=" + m_kwWordMap);Set<String> haveWords = getWord(content, MIN_MATCH_TYPE);System.out.println("haveWords=" + haveWords);}/*** 初始化DFA关键词容器* @param words*/@SuppressWarnings({ "rawtypes", "unchecked" })public static void init(Set<String> words) {// 预先 设置初始容量,以免扩容影响性能。Map wordMap = new HashMap(words.size());for (String word : words) {Map nowMap = wordMap;for (int i = 0; i < word.length(); i++) {// 转换成char型char keyChar = word.charAt(i);// 判断是否已经有一个map树,只有在一个词的首字符有用Object tempMap = nowMap.get(keyChar);if (tempMap != null) {// 存在,则共享一个map树根nowMap = (Map) tempMap;}// 不存在则构建一个map树,else {// 设置状态位Map<String, String> newMap = new HashMap<String, String>();// 判断是设置 0还是1newMap.put("isEnd", i == word.length() - 1 ? "1" : "0");// 给keyChar该字符设置状态位nowMap.put(keyChar, newMap);// 将状态位map赋值给nowMap,表示下一个字符的指针和状态位在同一个map里。nowMap = newMap;}}}// 上面始终修改的是nowMap,最后形成的是wordMap,原因是,预先wordMap赋值给了nowMap,// 使得wordMap和nowMap中的map地址值共享,更新了nowMap中的map就是更新了wordMap。m_kwWordMap = wordMap;}/*** 检索关键词* @param txt 被检索的文本* @param beginIndex  被检索文本的开始位置* @param matchType 匹配类型* @return 返回检索到的关键词长度,用于从文本中截取*/public static int checkWord(String txt, int beginIndex, int matchType) {// 匹配标识数默认为0Map nowMap = m_kwWordMap;int matchFlag = 0;int matchMaxFlag = 0;for (int i = beginIndex; i < txt.length(); i++) {char word = txt.charAt(i);// 获取指定keynowMap = (Map) nowMap.get(word);// 存在,则判断是否为最后一个if (nowMap != null) {// 找到相应key,匹配标识+1matchFlag++;// 如果为最后一个匹配规则,结束循环,返回匹配标识数if ("1".equals(nowMap.get("isEnd"))) {// 将matchFlag赋值给matchMaxFlag,为的是,// 后面要是继续按最大匹配原则匹配时,匹配不到则按最小匹配原则的结果为准。matchMaxFlag = matchFlag;// 最小规则,直接返回,最大规则还需继续查找if (MIN_MATCH_TYPE == matchType) {break;}}}// 不存在,直接返回else {break;}}return matchMaxFlag;}/*** 从文本中检索出关键词* @param txt 被检索的文本* @param matchType 匹配类型* @return*/public static Set<String> getWord(String txt, int matchType) {Set<String> set = new HashSet<String>();for (int i = 0; i < txt.length(); i++) {// 判断是否包含关键词,length > 0 有,且是关键词长度int length = checkWord(txt, i, matchType);// 存在,加入set中if (length > 0) {// 从原文中截取 并放入setset.add(txt.substring(i, i + length));// 减1的原因,因为for会自增i = i + length - 1;}}return set;}private static Map m_kwWordMap = null;// 最小匹配原则,如果关键词中有中国和中国人,文本内容为“我是中国人”,最小匹配原则匹配出中国public static final int MIN_MATCH_TYPE = 1;// 最大匹配原则,如果关键词中有中国和中国人,文本内容为“我是中国人”,最大匹配原则匹配出中国人public static final int MAX_MATCH_TYPE = 2;
}// wordMap={程={isEnd=0, 序={员={isEnd=1}, isEnd=0}}, 产={品={总={监={isEnd=1},      isEnd=0}, isEnd=0, 经={理={isEnd=1}, isEnd=0}}, isEnd=0}}
// haveWords=[产品经理]

五、总结

  1. DFA算法利用map套map的原理,极大程度上优化了检索性能,时间复杂度为小于等于O(n),n是被检索文本的长度。
  2. DFA实现的关键词过滤,性能不再受限于关键词的数量,只与被检索的文本长度有关。
  3. DFA关键词过滤,是精准过滤,无法实现模糊过滤,如我是*人*匹配单个字符,%匹配多个字符。

参考:https://www.cnblogs.com/twoheads/p/11349541.html 需要注意该文章中checkWord函数最大匹配原则时是有bug的,本人的示例已加matchMaxFlag更正。

PS: 如若文章中有错误理解,欢迎批评指正,同时非常期待你的评论、点赞和收藏。我是徐同学,愿与你共同进步!

如何实现一个高效的关键词过滤功能?——DFA算法相关推荐

  1. dfa算法 java_java实现敏感词过滤(DFA算法)

    小Alan在最近的开发中遇到了敏感词过滤,便去网上查阅了很多敏感词过滤的资料,在这里也和大家分享一下自己的理解. 在写之前,小Alan给大家推荐一篇来自http://cmsblogs.com/?p=1 ...

  2. 敏感词过滤 - DFA算法[确定有穷自动机]的Java 实现

    文章目录 敏感词过滤 - DFA算法[确定有穷自动机]的Java 实现 敏感词过滤 - DFA算法[确定有穷自动机]的Java 实现 代码如下 package utils;import com.goo ...

  3. 搜索不包含关键词_新手教程!百度关键词规划师功能详解

    SEM拓词方式有很多种,比如各平台自带的拓词工具.SEO工具如5118.艾奇拓词工具.搜索词报告等等.但其中,百度平台的拓词工具-关键词规划师应该说效率是最高的,除了拓展相关词外,百度关键词规划师还罗 ...

  4. python字典实现关键字检索_如何实现搜索框的关键词提示功能

    我们都使用过主流的搜索引擎,谷歌. bing,当然还有搜狗.百度之类.当你搜索某一关键词时,它会贴心在下拉框补全一些热门关键词,像下图这样: 搜索关键词提示 你点击某一关键词,页面就直接跳转到结果页面 ...

  5. 用python编写一个高效搜索代码工具

    用python编写一个高效搜索代码工具 大多码农在linux环境下使用grep+关键词的命令搜索自己想要的代码或者log文件.今天介绍用python如何编写一个更强大的搜索工具,windows下也适用 ...

  6. 搜索python代码的软件_用python编写一个高效搜索代码工具

    用python编写一个高效搜索代码工具 大多码农在linux环境下使用grep+关键词的命令搜索自己想要的代码或者log文件.今天介绍用python如何编写一个更强大的搜索工具,windows下也适用 ...

  7. SpringBoot+Redis 搞定搜索栏热搜、不雅文字过滤功能

    点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/qq_25838777/article/ details/109489767 使用java和redis实现一个简单的热搜功能,具 ...

  8. 大量的数据做字符串匹配_Python Flashtext 实现大数据集下高效的关键词查找和替换...

    通常,我们使用Python 在文本中进行关键词查找或替换时,会使用 re 模块以正则的形式实现.在文本数量.文本内容.关键词数量较小时,该方法能够满足我们程序的功能.性能需要.但当在大规模的文本或者对 ...

  9. THULAC:一个高效的中文词法分析工具包

    THULAC:一个高效的中文词法分析工具包 目录 软件简介 在线演示 编译和安装 使用方式 与代表性分词软件的性能对比 词性标记集 THULAC的不同配置 获取链接 注意事项 历史 开源协议 相关论文 ...

最新文章

  1. 2020,国产AI开源框架“亮剑”TensorFlow、PyTorch
  2. 利用frp进行内网穿透
  3. [cb]ScriptableWizard 创建向导
  4. 网站关键词优化如何控制其密度?
  5. [云炬ThinkPython阅读笔记]1.4 算术运算符
  6. 世界杯直播“三分天下”,视频平台如何实现高清直播?
  7. 4 SD配置-企业结构-定义-定义销售办公室
  8. python __repr__
  9. sql表格模型获取记录内容_SQL Server和BI –如何使用Excel记录表格模型
  10. android虚拟机的使用教程,Android 虚拟机可以这么用了 ?
  11. Linux上解压缩.gz、.bz2、.tar、.tar.gz、tar.xz后缀文件
  12. 2021/7/27 Ubuntu18.04 安装 PCL记录
  13. java用switch语句抽奖_Java使用带有switch语句的枚举
  14. vue项目中引用阿里云图标库
  15. 树莓派有线网络设置_树莓派通过网线实现与电脑共享网络
  16. AD域组策略安全管理
  17. 269. Alien Dictionary火星语字典(拓扑排序)
  18. 一秒快速修正 mysql ERROR 1406 (22001): Data too long for column ‘name‘ at row 1
  19. 南卡、ikf蓝牙耳机怎么样?南卡、ikf两款国产高性价比蓝牙耳机对比评测
  20. docker(七)容器与外部通信

热门文章

  1. 一加9pro Color OS 安卓12 安卓13 第三方 twrp 刷入
  2. 外发文件如何防泄密?
  3. Yii2融合EasySwoole的消息处理服务
  4. 怎样在INCISIVE里跑UVM1.2
  5. 小程序getApp() 被删除坑
  6. Keil 创建工程模板及精简第一个FreeRTOS程序
  7. 深度技术解读:Fomo3D 游戏第一轮是如何结束的
  8. Solr Searching
  9. The Open Group 开放流程自动化论坛 总监Ed Harrington 访谈录
  10. Sqoop 同步Parquet partition Hive表