1. 程式人生 > >使用Trie樹實現網站對使用者輸入的敏感詞打碼

使用Trie樹實現網站對使用者輸入的敏感詞打碼

使用Trie樹實現網站對使用者輸入的敏感詞打碼

什麼是Trie樹?

Trie樹,又稱單詞查詢樹,Trie樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計,排序和儲存大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:利用字串的公共字首來減少查詢時間,最大限度地減少無謂的字串比較,查詢效率比雜湊樹高。

Trie樹的核心思想是空間換時間,利用字串的公共字首來降低查詢時間的開銷以達到提高效率的目的。
還在路上,稍等...
使用Trie樹資料結構可以匹配多個關鍵詞,速度快。Trie樹的最壞空間複雜度為O(m^n),最壞時間複雜度為O(n)。從而可以利用該資料結構把一段文字中敏感詞替換為***或者刪除掉

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStream;
import
java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; @Service public class SensitiveService implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(SensitiveService.class); private TrieNode rootNode = new TrieNode(); public static
void main(String[] args) { SensitiveService s = new SensitiveService(); s.addWord("色情"); s.addWord("賭博"); String test = "你好啊色*情傢伙,竟然賭博啊,你是個壞人!"; System.out.println(s.filter(test)); } public String filter(String text) { StringBuilder sb = new StringBuilder(); if (StringUtils.isEmpty(text)) { return text; } String replace = "***"; TrieNode curNode = rootNode; int begin = 0; int position = 0; while (position < text.length()) { char c = text.charAt(position); //如果不是東亞文字,如空格,則跳過在trie樹中匹配 if (isSymbol(c)) { if (curNode == rootNode) { sb.append(c); ++begin; } ++position; continue; } curNode = curNode.getSubNode(c); /** * 如果curNode等於Null,則說明在trie樹中找不到下個字元,這個詞不會是敏感詞 * 換句話說,trie樹中沒有這個子節點,說明begin開頭的字串不是敏感詞, * 把begin這個字元加入過濾後的字串中,同時字首樹指標指回開頭 */ if (curNode == null) { sb.append(text.charAt(begin)); position = begin + 1; begin = position; curNode = rootNode; } else if (curNode.isKeywordEnd()) { //發現一個敏感詞 sb.append(replace); position = position + 1; begin = position; curNode = rootNode; } else { ++position; } } sb.append(text.substring(begin)); return sb.toString(); } @Override public void afterPropertiesSet() throws Exception { try { InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("SensitiveWords.txt"); InputStreamReader reader = new InputStreamReader(is); BufferedReader bufferedReader = new BufferedReader(reader); String lineTxt; while ((lineTxt = bufferedReader.readLine()) != null) { addWord(lineTxt.trim()); } reader.close(); } catch (Exception e) { logger.error("讀取過濾的敏感詞檔案失敗" + e.getMessage()); } } //構建trie樹,在字首樹中新增敏感詞 private void addWord(String lineTxt) { TrieNode curNode = rootNode; for (int i = 0; i < lineTxt.length(); ++i) { Character c = lineTxt.charAt(i); TrieNode node = curNode.getSubNode(c); if (node == null) { node = new TrieNode(); curNode.addSubNode(c, node); } curNode = node; if (i == lineTxt.length() - 1) { curNode.setKeywordEnd(true); } } } private class TrieNode { private boolean end = false; //子節點 private Map<Character, TrieNode> subNodes = new HashMap<>(); public void addSubNode(Character key, TrieNode node) { subNodes.put(key, node); } TrieNode getSubNode(Character key) { return subNodes.get(key); } boolean isKeywordEnd() { return end; } void setKeywordEnd(boolean end) { this.end = end; } } //返回true表示不是東亞文字 private boolean isSymbol(char c) { int i = (int) c; //東亞文字0x2E80-0x9FFF return !CharUtils.isAsciiAlphanumeric(c) && (i < 0x2E80 || i > 0x9FFF); } }

該程式執行結果如下圖:
這裡寫圖片描述