[轉*摘要*總結]敏感詞過濾的算法原理之DFA算法

問題背景

敏感詞、文字過濾是一個網站必不可少的功能,過濾的關鍵是用戶輸入內容與敏感字庫的匹配。
對於字符串匹配,一般的方法是字符串子串包含判斷、正則表達式判斷,但對於用戶輸入的大量內容,它們的效率是非常低的。Google和百度搜索文字過濾算法時我找到了一個比較好的算法DFA算法。

  • 實際項目中,對於整句的匹配我們採用的仍是正則表達式,因爲整句詞庫比較少;對於單詞屏蔽,我們採用的是DFA算法來處理,因爲單詞字庫是萬級以上的,DFA算法簡單高效。

DFA算法簡介

DFA即Deterministic Finite Automaton,也就是確定有窮自動機,它是是通過event和當前的state得到下一個state,即event+state=nextstate,因爲只有狀態的變化,沒有過多的運算,所以DFA很高效。

DFA算法處理敏感詞過濾的核心就是:將關鍵詞轉換成一個個類似下圖的敏感字樹用於搜索匹配。

敏感詞:日本人、日本鬼子、日本男人
對應敏感詞樹結構:
在這裏插入圖片描述

搜索時:由於我們將敏感詞庫構建成了一個類似上圖的一顆一顆的樹,這樣我們判斷一個詞是否爲敏感詞時就大大減少了檢索的匹配範圍。比如我們要判斷日本人,根據第一個字我們就可以確認需要檢索的是那棵樹,然後再在這棵樹中進行檢索。

java實現DFA算法(HashMap版)

Step1. 構建敏感詞搜索樹
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void addSensitiveWordToHashMap(Set<String> keyWordSet) {
        sensitiveWordMap = new HashMap(keyWordSet.size());     //初始化敏感詞容器,減少擴容操作
        String key = null;  
        Map nowMap = null;
        Map<String, String> newWorMap = null;
        //迭代keyWordSet
        Iterator<String> iterator = keyWordSet.iterator();
        while(iterator.hasNext()){
            key = iterator.next();    //關鍵字
            nowMap = sensitiveWordMap;
            for(int i = 0 ; i < key.length() ; i++){
                char keyChar = key.charAt(i);       //轉換成char型
                Object wordMap = nowMap.get(keyChar);       //獲取
                
                if(wordMap != null){        //如果存在該key,直接賦值
                    nowMap = (Map) wordMap;
                }
                else{     //不存在則,則構建一個map,同時將isEnd設置爲0,因爲他不是最後一個
                    newWorMap = new HashMap<String,String>();
                    newWorMap.put("isEnd", "0");     //不是最後一個
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }
                
                if(i == key.length() - 1){
                    nowMap.put("isEnd", "1");    //最後一個
                }
            }
        }
    }


運行得到的hashMap結構如下:

{
	"五": {
		"星": {
			"紅": {
				"isEnd": 0,
				"旗": {
					"isEnd": 1
				}
			},
			"isEnd": 0
		},
		"isEnd": 0
	},
	"中": {
		"isEnd": 0,
		"國": {
			"isEnd": 0,
			"人": {
				"isEnd": 1
			},
			"男": {
				"isEnd": 0,
				"人": {
					"isEnd": 1
				}
			}
		}

	}
}
Step2. 檢查文本中的敏感詞
    @SuppressWarnings({ "rawtypes"})
    public int CheckSensitiveWord(String txt,int beginIndex,int matchType){
        boolean  flag = false;    //敏感詞結束標識位:用於敏感詞只有1位的情況
        int matchFlag = 0;     //匹配標識數默認爲0
        char word = 0;
        Map nowMap = sensitiveWordMap;
        for(int i = beginIndex; i < txt.length() ; i++){
            word = txt.charAt(i);
            nowMap = (Map) nowMap.get(word);     //獲取指定key
            if(nowMap != null){     //存在,則判斷是否爲最後一個
                matchFlag++;     //找到相應key,匹配標識+1 
                if("1".equals(nowMap.get("isEnd"))){       //如果爲最後一個匹配規則,結束循環,返回匹配標識數
                    flag = true;       //結束標誌位爲true   
                    if(SensitivewordFilter.minMatchTYpe == matchType){    //最小規則,直接返回,最大規則還需繼續查找
                        break;
                    }
                }
            }
            else{     //不存在,直接返回
                break;
            }
        }
        if(matchFlag < 2 && !flag){     
            matchFlag = 0;
        }
        return matchFlag;
    }


補充:中文漢字的繁簡體轉換工具包opencc4j

opencc4j是一個我們實際用過值得推薦的一個繁簡體轉換工具包,之前我們在也有用過一些通過繁簡體組成的字典Map來進行轉換的方案,但都出現了漏詞、轉換失敗的問題,原因是字典不全等原因。

opencc4j使用很簡單:
step1:maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>opencc4j</artifactId>
    <version>1.0.2</version>
</dependency>

step2:直接使用ZhConverterUtil靜態方法

// 簡體轉繁體:
String original = "爲中華之崛起而讀書";
String result = ZhConverterUtil.convertToTraditional(original);
// result:為中華之崛起而讀書

// 繁體轉簡體:
String original = "為中華之崛起而讀書";
String result = ZhConverterUtil.convertToSimple(original);
// result:爲中華之崛起而讀書

tips: 實例使用中需要注意過濾敏感詞庫的特殊符號,特別是涉及到正則表達式匹配的邏輯時;爲了準確性,也可以按需要配置自己的停頓詞,百名單字庫等,搜索時跳過這些詞,提高屏蔽的準確性。

參考文獻

http://www.iteye.com/topic/336577
https://www.cnblogs.com/twoheads/p/11349541.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章