如何实现一个高效的关键词过滤功能?——DFA算法
文章目录
- 一、前言
- 二、何为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=[产品经理]
五、总结
- DFA算法利用map套map的原理,极大程度上优化了检索性能,时间复杂度为小于等于O(n),n是被检索文本的长度。
- DFA实现的关键词过滤,性能不再受限于关键词的数量,只与被检索的文本长度有关。
- DFA关键词过滤,是精准过滤,无法实现模糊过滤,如
我是*人
,*
匹配单个字符,%
匹配多个字符。
参考:https://www.cnblogs.com/twoheads/p/11349541.html 需要注意该文章中checkWord
函数最大匹配原则时是有bug的,本人的示例已加matchMaxFlag
更正。
PS: 如若文章中有错误理解,欢迎批评指正,同时非常期待你的评论、点赞和收藏。我是徐同学,愿与你共同进步!
如何实现一个高效的关键词过滤功能?——DFA算法相关推荐
- dfa算法 java_java实现敏感词过滤(DFA算法)
小Alan在最近的开发中遇到了敏感词过滤,便去网上查阅了很多敏感词过滤的资料,在这里也和大家分享一下自己的理解. 在写之前,小Alan给大家推荐一篇来自http://cmsblogs.com/?p=1 ...
- 敏感词过滤 - DFA算法[确定有穷自动机]的Java 实现
文章目录 敏感词过滤 - DFA算法[确定有穷自动机]的Java 实现 敏感词过滤 - DFA算法[确定有穷自动机]的Java 实现 代码如下 package utils;import com.goo ...
- 搜索不包含关键词_新手教程!百度关键词规划师功能详解
SEM拓词方式有很多种,比如各平台自带的拓词工具.SEO工具如5118.艾奇拓词工具.搜索词报告等等.但其中,百度平台的拓词工具-关键词规划师应该说效率是最高的,除了拓展相关词外,百度关键词规划师还罗 ...
- python字典实现关键字检索_如何实现搜索框的关键词提示功能
我们都使用过主流的搜索引擎,谷歌. bing,当然还有搜狗.百度之类.当你搜索某一关键词时,它会贴心在下拉框补全一些热门关键词,像下图这样: 搜索关键词提示 你点击某一关键词,页面就直接跳转到结果页面 ...
- 用python编写一个高效搜索代码工具
用python编写一个高效搜索代码工具 大多码农在linux环境下使用grep+关键词的命令搜索自己想要的代码或者log文件.今天介绍用python如何编写一个更强大的搜索工具,windows下也适用 ...
- 搜索python代码的软件_用python编写一个高效搜索代码工具
用python编写一个高效搜索代码工具 大多码农在linux环境下使用grep+关键词的命令搜索自己想要的代码或者log文件.今天介绍用python如何编写一个更强大的搜索工具,windows下也适用 ...
- SpringBoot+Redis 搞定搜索栏热搜、不雅文字过滤功能
点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/qq_25838777/article/ details/109489767 使用java和redis实现一个简单的热搜功能,具 ...
- 大量的数据做字符串匹配_Python Flashtext 实现大数据集下高效的关键词查找和替换...
通常,我们使用Python 在文本中进行关键词查找或替换时,会使用 re 模块以正则的形式实现.在文本数量.文本内容.关键词数量较小时,该方法能够满足我们程序的功能.性能需要.但当在大规模的文本或者对 ...
- THULAC:一个高效的中文词法分析工具包
THULAC:一个高效的中文词法分析工具包 目录 软件简介 在线演示 编译和安装 使用方式 与代表性分词软件的性能对比 词性标记集 THULAC的不同配置 获取链接 注意事项 历史 开源协议 相关论文 ...
最新文章
- 2020,国产AI开源框架“亮剑”TensorFlow、PyTorch
- 利用frp进行内网穿透
- [cb]ScriptableWizard 创建向导
- 网站关键词优化如何控制其密度?
- [云炬ThinkPython阅读笔记]1.4 算术运算符
- 世界杯直播“三分天下”,视频平台如何实现高清直播?
- 4 SD配置-企业结构-定义-定义销售办公室
- python __repr__
- sql表格模型获取记录内容_SQL Server和BI –如何使用Excel记录表格模型
- android虚拟机的使用教程,Android 虚拟机可以这么用了 ?
- Linux上解压缩.gz、.bz2、.tar、.tar.gz、tar.xz后缀文件
- 2021/7/27 Ubuntu18.04 安装 PCL记录
- java用switch语句抽奖_Java使用带有switch语句的枚举
- vue项目中引用阿里云图标库
- 树莓派有线网络设置_树莓派通过网线实现与电脑共享网络
- AD域组策略安全管理
- 269. Alien Dictionary火星语字典(拓扑排序)
- 一秒快速修正 mysql ERROR 1406 (22001): Data too long for column ‘name‘ at row 1
- 南卡、ikf蓝牙耳机怎么样?南卡、ikf两款国产高性价比蓝牙耳机对比评测
- docker(七)容器与外部通信