js實現敏感詞過濾算法

大半個月沒有更新了,由於最近有點忙(實際上是懶)javascript

最近弄了一個用戶發表評論的功能,用戶上傳了評論,再文章下能夠看到本身的評論,但做爲社會主義接班人,踐行社會主義核心價值觀,因此給評論敏感詞過濾的功能不可少,在網上找了資料,發現已經有很是成熟的解決方案。 經常使用的方案用這麼兩種java

  1. 全文搜索,逐個匹配。這種聽起來就不夠高大上,在數據量大的狀況下,會有效率問題,文末有比較
  2. DFA算法-肯定有限狀態自動機 附上百科連接 肯定有限狀態自動機

DFA算法介紹

DFA是一種計算模型,數據源是一個有限個集合,經過當前狀態和事件來肯定下一個狀態,即 狀態+事件=下一狀態,由此逐步構建一個有向圖,其中的節點就是狀態,因此在DFA算法中只有查找和判斷,沒有複雜的計算,從而提升算法效率算法

參考文章 Java實現敏感詞過濾數據結構

實現邏輯

構造數據結構

將敏感詞轉換成樹結構,舉例敏感詞有着這麼幾個 ['日本鬼子','日本人','日本男人'],那麼數據結構以下(圖片引用參考文章)測試

數據結構

每一個文字是一個節點,連續的節點組成一個詞,日本人對應的就是中間的那條鏈,咱們可使用對象或者map來構建樹,這裏的栗子採用map構建節點,每一個節點中有個狀態標識,用來表示當前節點是否是最後一個,每條鏈路必需要有個終點節點,先來看下構建節點的流程圖ui

流程圖

判斷邏輯

先從文本的第一個字開始檢查,好比你我是日本鬼子,第一個字 ,在樹的第一層找不到這個節點,那麼繼續找第二個字,到了的時候,第一層節點找到了,那麼接着下一層節點中查找,同時判斷這個節點是否是結尾節點,如果結尾節點,則匹配成功了,反之繼續匹配spa

代碼實現

####構造數據結構.net

/** * @description * 構造敏感詞map * @private * @returns */
private makeSensitiveMap(sensitiveWordList) {
    // 構造根節點
    const result = new Map();
    for (const word of sensitiveWordList) {
        let map = result;
        for (let i = 0; i < word.length; i++) {
            // 依次獲取字
            const char = word.charAt(i);
            // 判斷是否存在
            if (map.get(char)) {
                // 獲取下一層節點
                map = map.get(char);
            } else {
                // 將當前節點設置爲非結尾節點
                if (map.get('laster') === true) {
                    map.set('laster', false);
                }
                const item = new Map();
                // 新增節點默認爲結尾節點
                item.set('laster', true);
                map.set(char, item);
                map = map.get(char);
            }
        }

    }
    return result;
}
複製代碼

最終map結構以下code

結構

查找敏感詞

/** * @description * 檢查敏感詞是否存在 * @private * @param {any} txt * @param {any} index * @returns */
private checkSensitiveWord(sensitiveMap, txt, index) {
    let currentMap = sensitiveMap;
    let flag = false;
    let wordNum = 0;//記錄過濾
    let sensitiveWord = ''; //記錄過濾出來的敏感詞
    for (let i = index; i < txt.length; i++) {
        const word = txt.charAt(i);
        currentMap = currentMap.get(word);
        if (currentMap) {
            wordNum++;
            sensitiveWord += word;
            if (currentMap.get('laster') === true) {
                // 表示已到詞的結尾
                flag = true;
                break;
            }
        } else {
            break;
        }
    }
    // 兩字成詞
    if (wordNum < 2) {
        flag = false;
    }
    return { flag, sensitiveWord };
}
/** * @description * 判斷文本中是否存在敏感詞 * @param {any} txt * @returns */
public filterSensitiveWord(txt, sensitiveMap) {
    let matchResult = { flag: false, sensitiveWord: '' };
    // 過濾掉除了中文、英文、數字以外的
    const txtTrim = txt.replace(/[^\u4e00-\u9fa5\u0030-\u0039\u0061-\u007a\u0041-\u005a]+/g, '');
    for (let i = 0; i < txtTrim.length; i++) {
        matchResult = checkSensitiveWord(sensitiveMap, txtTrim, i);
        if (matchResult.flag) {
            console.log(`sensitiveWord:${matchResult.sensitiveWord}`);
            break;
        }
    }
    return matchResult;
}
複製代碼

效率

爲了看出DFA的效率,我作了個簡單的小測試,測試的文本長度爲5095個漢字,敏感詞詞庫中有2000個敏感詞,比較的算法分別爲 DFA算法 和 String原生對象提供的 indexOfAPI作比較regexp

// 簡單的字符串匹配-indexOf
ensitiveWords.forEach((word) => {
    if (ss.indexOf(word) !== -1) {
        console.log(word)
    }
})
複製代碼

分別將兩個算法執行100次,獲得以下結果

比較結果

可直觀看出,DFA的平均耗時是在1ms左右,最大爲5ms;indexOf方式的平均耗時在9ms左右,最大爲14ms,因此DFA效率上仍是很是明顯有優點的。

相關文章
相關標籤/搜索