• JAVA做敏感词统计——DFA 算法


    DFA,全称 Deterministic Finite Automaton 即确定有穷自动机:从一个状态通过一系列的事件转换到另一个状态,即 state -> event -> state。

      确定:状态以及引起状态转换的事件都是可确定的,不存在“意外”。
      有穷:状态以及事件的数量都是可穷举的
    详细的算法大家可以自行鸟姐下哈。今天咱们主要讲下DFA算法进行敏感词统计

    比如说有以下这些敏感词

    【二傻子,不太行,二傻子吧?】

    咱们先分析下。如果不用DFA算法。咱们就得用正则表达式匹配。看下是否包含这两个词。

    但是这样的话效率太低了。会随着被匹配内容的增加进行指数型增加(也就是O(2ⁿ))

    接下咱们可以想着优化下

    可以发现如果这个文本都不包含`二`就更不会包含 二傻子

    以此类推 包含 `二` 但是后边不是 `傻` 那也不包含敏感词  再以此类推

    转成Java思维 

    {二={End=0, 傻={子={End=1}, End=0,吧={?={End=1}}}  必须把链路走完才是敏感词。也就是有1

    上边这个json就可以用Java当中的Map来实现。上代码

      1 import java.io.BufferedReader;
      2 import java.io.File;
      3 import java.io.FileInputStream;
      4 import java.io.InputStreamReader;
      5 import java.util.*;
      6 
      7 /**
      8  * 敏感词对象
      9  */
     10 @Service
     11 public class SensitiveWord {
     12 
     13     //分布式锁
     14     private static IDistributedLock distributedLock;
     15 
     16     @Autowired
     17     public void setIDistributedLock(IDistributedLock distributedLock) {
     18         SensitiveWord.distributedLock = distributedLock;
     19     }
     20 
     21 
     22     /**
     23      * 铭感词状态 false 未初始化 ture已初始化
     24      */
     25     public static boolean status;
     26     public static HashMap sensitiveWordMap;
     27 
     28     public SensitiveWord() {
     29         super();
     30     }
     31 
     32     /**
     33      *
     34      */
     35     @SuppressWarnings("rawtypes")
     36     public static Map initKeyWord() {
     37         String distributedLockKey = "information:applyCredit:admin:wscs";
     38         try {
     39 
     40             if (!distributedLock.tryLock(distributedLockKey, 3, -1)) {
     41                 throw BusinessException.of(CommonCodeMsg.TOO_FREQUENT);
     42             }
     43             status = false;
     44             //读取敏感词库
     45             Set<String> keyWordSet = readSensitiveWordFile();
     46             //将敏感词库加入到HashMap中
     47             addSensitiveWordToHashMap(keyWordSet);
     48 
     49         } catch (Exception e) {
     50             e.printStackTrace();
     51         } finally {
     52             status = true;
     53             // 待事务提交后再释放锁
     54             SpringUtils.afterCompletion(() -> distributedLock.release(distributedLockKey));
     55         }
     56         return sensitiveWordMap;
     57     }
     58 
     59 
     60     /**
     61      * 读取敏感词库,将敏感词放入HashSet中,构建一个DFA算法模型:<br>
     62      * 中 = {
     63      * isEnd = 0
     64      * 国 = {<br>
     65      * isEnd = 1
     66      * 人 = {isEnd = 0
     67      * 民 = {isEnd = 1}
     68      * }
     69      * 男  = {
     70      * isEnd = 0
     71      * 人 = {
     72      * isEnd = 1
     73      * }
     74      * }
     75      * }
     76      * }
     77      * 五 = {
     78      * isEnd = 0
     79      * 星 = {
     80      * isEnd = 0
     81      * 红 = {
     82      * isEnd = 0
     83      * 旗 = {
     84      * isEnd = 1
     85      * }
     86      * }
     87      * }
     88      * }
     89      *
     90      * @param keyWordSet 敏感词库
     91      * @version 1.0
     92      */
     93     @SuppressWarnings({"rawtypes", "unchecked"})
     94     public static void addSensitiveWordToHashMap(Set<String> keyWordSet) {
     95         sensitiveWordMap = new HashMap(keyWordSet.size());     //初始化敏感词容器,减少扩容操作
     96         String key = null;
     97         Map nowMap = null;
     98         Map<String, String> newWorMap = null;
     99         //迭代keyWordSet
    100         Iterator<String> iterator = keyWordSet.iterator();
    101         while (iterator.hasNext()) {
    102             key = iterator.next();    //关键字
    103             nowMap = sensitiveWordMap;
    104             for (int i = 0; i < key.length(); i++) {
    105                 char keyChar = key.charAt(i);       //转换成char型
    106                 Object wordMap = nowMap.get(keyChar);       //获取
    107 
    108                 if (wordMap != null) {        //如果存在该key,直接赋值
    109                     nowMap = (Map) wordMap;
    110                 } else {     //不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
    111                     newWorMap = new HashMap<String, String>();
    112                     newWorMap.put("isEnd", "0");     //不是最后一个
    113                     nowMap.put(keyChar, newWorMap);
    114                     nowMap = newWorMap;
    115                 }
    116 
    117                 if (i == key.length() - 1) {
    118                     nowMap.put("isEnd", "1");    //最后一个
    119                 }
    120             }
    121         }
    122     }
    123 
    124     /**
    125      * 读取敏感词库中的内容,将内容添加到set集合中
    126      *
    127      * @return
    128      * @throws Exception
    129      */
    130     @SuppressWarnings("resource")
    131     public static Set<String> readSensitiveWordFile() throws Exception {
    132         Set<String> set = null;
    133 
    134         File file = new File("D:\\SensitiveWord.txt");    //读取文件
    135         InputStreamReader read = new InputStreamReader(new FileInputStream(file), "UTF-8");
    136         try {
    137             if (file.isFile() && file.exists()) {      //文件流是否存在
    138                 set = new HashSet<String>();
    139                 BufferedReader bufferedReader = new BufferedReader(read);
    140                 String txt = null;
    141                 while ((txt = bufferedReader.readLine()) != null) {    //读取文件,将文件内容放入到set中
    142                     set.add(txt);
    143                 }
    144             } else {         //不存在抛出异常信息
    145                 throw new Exception("敏感词库文件不存在");
    146             }
    147         } catch (Exception e) {
    148             throw e;
    149         } finally {
    150             read.close();     //关闭文件流
    151         }
    152         return set;
    153     }
    154 
    155 }
    View Code

     酱紫就会把所有的敏感词都先缓存。然后再进行敏感词过滤

     1 /**
     2      * 敏感词过滤
     3      */
     4     public void sensitiveWordFilter() {
     5 
     6         String text = "这是一段需要检测的文字。知道了吗?二傻子?";
     7         for (int i = 0; i < text.length(); i++) {
     8             boolean isExist = this.checkSensitiveWord(text, i);
     9             //判断是否包含敏感字符
    10             if (isExist) {
    11                 System.out.println("你惨了,你说话都是星星");
    12                 return;
    13             }
    14 
    15         }
    16 
    17     }
    18 
    19 
    20     /**
    21      * 检查文字中是否包含敏感字符,检查规则如下:<br>
    22      *
    23      * @param txt        需要检测的文字
    24      * @param beginIndex 开始检测的位置
    25      */
    26     @SuppressWarnings({"rawtypes"})
    27     public boolean checkSensitiveWord(String txt, int beginIndex) {
    28         Map nowMap = SensitiveWord.sensitiveWordMap;
    29         for (int i = beginIndex; i < txt.length(); i++) {
    30             char word = txt.charAt(i);
    31             nowMap = (Map) nowMap.get(word);     //获取指定key
    32             if (nowMap != null) {     //存在,则判断是否为最后一个
    33                 if ("1".equals(nowMap.get("isEnd"))) {
    34                     return true;       //结束标志位为true
    35                 }
    36             } else {
    37                 //不存在,直接返回
    38                 break;
    39             }
    40         }
    41         return false;
    42     }
    View Code

    可以看到这样做,时间复杂度就会降到O(n2)。可以说是非常的不错啦

    课后作业

    1.统计敏感词字数

    2.把敏感词替换成***

  • 相关阅读:
    stm32芯片上两个点 那个是一脚
    MLX90640 热红外成像仪 热源检测 电子检修 热水管探测
    USB继电器 开关 2路 5V 单片机STM32 Arduino 电器单独DC供电
    ARM仿真下载调试器 SWD编程器 Jlink OB下载器 超STLINK 代替V8
    Kotlin+Jetpack Compose+Volley+ViewModel Android开发
    TortoiseGit MergeTool 文件比对工具替换为 DiffMerge
    mysql 行转列 多行转一行
    MySQL using index condition
    PHP获取参数时, 解耦其默认值的处理逻辑
    centos7安装rabbitmq3.9.5单机部署
  • 原文地址:https://www.cnblogs.com/zbzdqsmh/p/15633031.html
Copyright © 2020-2023  润新知