天天看點

利用 DFA 算法實作文字過濾

一、DFA 算法簡介

在實作文字過濾的算法中,DFA是唯一比較好的實作算法。

DFA 全稱為:Deterministic Finite Automaton,即确定有窮自動機。其特征為:有一個有限狀态集合和一些從一個狀态通向另一個狀态的邊,每條邊上标記有一個符号,其中一個狀态是初态,某些狀态是終态。但不同于不确定的有限自動機,DFA 中不會有從同一狀态出發的兩條邊标志有相同的符号。

利用 DFA 算法實作文字過濾

簡單點說就是,它是是通過 event 和目前的 state 得到下一個 state,即 event + state= nextstate。了解為系統中有多個節點,通過傳遞進入的 event,來确定走哪個路由至另一個節點,而節點是有限的。

二、DEA 算法實踐敏感詞過濾

1. 敏感詞庫構造

以王八蛋和王八羔子兩個敏感詞來進行描述,首先建構敏感詞庫,該詞庫名稱為SensitiveMap,這兩個詞的二叉樹構造為:

利用 DFA 算法實作文字過濾

用 hash 表構造為:

{
    "王":{
        "isEnd":"0",
        "八":{
            "羔":{
                "子":{
                    "isEnd":"1"
                },
                "isEnd":"0"
            },
            "isEnd":"0",
            "蛋":{
                "isEnd":"1"
            }
        }
    }
}
           

怎麼用代碼實作這種資料結構呢?

/**
     * 讀取敏感詞庫,将敏感詞放入HashSet中,建構一個DFA算法模型
     *
     * @param keyWordSet 敏感詞庫
     */
    public Map<String, Object> addSensitiveWordToHashMap(Set<String> keyWordSet) {
        //初始化敏感詞容器,減少擴容操作
        Map<String, Object> map = new HashMap(Math.max((int) (keyWordSet.size() / .75f) + 1, 16));
        //疊代keyWordSet
        for (String aKeyWordSet : keyWordSet) {
            Map nowMap = map;
            for (int i = 0; i < aKeyWordSet.length(); i++) {
                //轉換成char型
                char keyChar = aKeyWordSet.charAt(i);
                //擷取
                Object wordMap = nowMap.get(keyChar);
                //如果存在該key,直接指派
                if (wordMap != null) {
                    nowMap = (Map) wordMap;
                } else {     //不存在則,則建構一個map,同時将isEnd設定為0
                    Map<String, String> newWorMap = new HashMap<>(3);
                    newWorMap.put("isEnd", "0");
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }
                //判斷最後一個
                if (i == aKeyWordSet.length() - 1) {
                    nowMap.put("isEnd", "1");
                }
            }
        }
        return map;
    }
           

2. 敏感詞過濾

以上面例子構造出來的 SensitiveMap 為敏感詞庫進行示意,假設這裡輸入的關鍵字為:王八不好,流程圖如下:

利用 DFA 算法實作文字過濾

怎麼用代碼實作這個流程圖邏輯呢?

/**
     * 查找字元串中是否包含敏感字元
     *
     * @param txt 輸入的字元串
     * @return 如果存在,則傳回敏感字元串;不存在,則傳回空字元串
     */
    public static String findSensitiveWord(String txt) {
        SensitiveWordInit sensitiveWordInit = SpringContextHolder.getBean(SensitiveWordInit.class);
        Map<String, Object> sensitiveWordMap = sensitiveWordInit.getSensitiveWordMap();
        StringBuilder sensitiveWord = new StringBuilder();
        // 敏感詞結束标志位,表示比對到了最後一位
        boolean flag = false;
        for (int i = 0; i < txt.length(); i++) {
            char word = txt.charAt(i);
            // 擷取指定 key
            sensitiveWordMap = (Map) sensitiveWordMap.get(word);
            // 不存在,直接傳回沒有敏感詞
            if (sensitiveWordMap == null) {
                break;
            }
            //存在,存儲該敏感詞,并判斷是否為最後一個
            sensitiveWord.append(word);
            //如果為最後一個比對規則,結束循環
            if ("1".equals(sensitiveWordMap.get("isEnd"))) {
                flag = true;
                break;
            }
        }
        // 表示比對到了完整敏感詞
        if (flag == true) {
            return sensitiveWord.toString();
        }
        return "";
    }
           

三、優化思路

對于“王*八&&蛋”這樣的詞,中間填充了無意義的字元來混淆,在我們做敏感詞搜尋時,同樣應該做一個無意義詞的過濾,當循環到這類無意義的字元時進行跳過,避免幹擾。