1. 程式人生 > >敏感詞過濾演算法:字首樹演算法

敏感詞過濾演算法:字首樹演算法

背景

平時我們在逛貼吧、牛客網的時候,我們經常可以看到一些形如 “***”的符號,通過上下文,我們也可以很容易猜到這些詞原來是罵人的話,只是被系統和諧了。那麼這是如何實現的呢?作為普通人,我們最先想到的一種辦法就是把所有敏感串存入一個列表中,然後使用者每發一條內容後臺就把該內容與敏感串列表的每一項進行匹配,然後把匹配的字元進行和諧。顯然這樣的效率是很低的。非常影響效能,那麼我們有沒有其他的演算法呢?這就是我這篇博文打算介紹的。

原理講解

1.首先建立個敏感詞字首樹

根節點為空

2.準備好待處理字串: 哈哈大王八子大豬蹄子哦 ,宣告三個指標,分別指向字首樹的根節點以及待處理字串的開始字元

3.position指向的字元與根節點的所有子節點進行匹配,不匹配,position 和 begin分別指向待處理字串的下一個字元,tempNode依舊指向 根節點

4.依舊不匹配,position 和begin繼續向前走一位,指向“”,treeNode依舊指向根節點

 

5.此時 根節點有一個子節點 與 position指向的字元相等,都為‘’,則tempNode 指向該節點,同時position前進一步,指向‘

6.此時把position指向的‘’ 和 tempNode的所有子節點進行匹配,匹配失敗,說明 從begin起頭所有串是不存在敏感詞的,可以直接輸出。此時begin前進一位,position回退到begin的位置,tempNode回退到根節點

7.此時再把position指向的‘王’與tempNode的所有子節點進行匹配,匹配成功,所以tempNode指向該節點,同時position前進一位,指向''

8.此時再把position指向的‘’ 與tempNode的所有子節點進行匹配,匹配成功,此時tempNode 指向它的子節點‘’,同時position前進一位。

 

9.繼續把position指向的字元與tempNode的所有子節點進行匹配,匹配失敗。說明以begin起頭的不存在非法字元,可以加入到結果集中。 此時begin向前走一位,position回退到begin的位置,同時tempNode回退到根節點。

10.同理,可以發現子'子'不匹配,則直接把它加入結果集,同時position 和begin 向前走一位,tempNode指向根節點。

此時position指向 ‘’,與tempNode的所有 子節點進行匹配,匹配成功,則position和tempNode都走一位,迴圈執行....

直到position指向‘’,tempNode指向‘蹄’

 11.此時把position與tempNode的所有子節點進行匹配,匹配成功,tempNode指向它的子節點‘子’,此時檢查發現tempNode是敏感詞樹的葉子節點,說明從begin+1開始的位置 到 position這段是敏感詞,用和諧詞替換掉。替換之後position前進一位,begin跳到position的位置,tempNode回退到根節點

以上,就是全部流程啦,理解了之後看程式碼就簡單多啦

程式碼講解

1.字首樹節點結構

private class TreeNode{

        //是否最後一個字
        private boolean isKeyWordsEnd = false;

        //子節點
        private Map<Character,TreeNode> subNodes = new HashMap<>();

        public void addSubNode(Character key, TreeNode node){
            subNodes.put(key,node);
        }

        public TreeNode getSubNode(Character key){
            return subNodes.get(key);
        }

        public boolean isKeyWordsEnd(){
            return isKeyWordsEnd;
        }

        public void setKeyWordsEnd(Boolean end){
            isKeyWordsEnd = end;
        }
    }

2.構建字首樹的方法

public void addSensitiveWord(String words){

        TreeNode tempNode = rootNode;

        for(int i = 0;  i < words.length(); i++){

            Character c = words.charAt(i);
            if(!isSymbol(c)){
                continue;
            }

            TreeNode node = tempNode.getSubNode(c);
            if (node == null){
                node = new TreeNode();
                tempNode.addSubNode(c,node);
            }
            // 指標移動
            tempNode = node;

            //如果到了最後一個字元
            if(i == words.length() -1){
                tempNode.setKeyWordsEnd(true);
            }

        }

    }

3.演算法具體實現

public String filter(String text){

        if (StringUtils.isEmpty(text)){
            return text;
        }

        String sensitiveWords = "***";
        StringBuilder result = new StringBuilder();

        TreeNode tempNode = rootNode;
        int begin = 0;
        int position = 0;

        while (position < text.length()){

            Character c = text.charAt(position);

            //如果非東亞字元,則直接跳過 ??
            if(!isSymbol(c)){ //每次
                if(tempNode == rootNode){
                    result.append(c);
                    begin++;
                }
                position++;
                continue;
            }

            tempNode = tempNode.getSubNode(c);

            //如果匹配失敗
            if(tempNode == null){
                //說明以begin起頭的那一段不存在非法詞彙
                result.append(text.charAt(begin));
                begin++;
                position = begin;
                tempNode = rootNode;
                continue;
            }else if(tempNode.isKeyWordsEnd()){
                //替換敏感詞
                result.append(sensitiveWords);

                position++;
                begin = position;
                tempNode = rootNode;
            }else {
                position++;
            }

        }
        result.append(text.substring(begin)); //把剩下的動加入合法集

        return result.toString();
    }

小結

最近一直在做專案,所以有一段時間沒寫文章了,專案也快完成了,就把在專案中使用的一個演算法做了下總結,希望能給讀者一些幫助。