日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

敏感词过滤算法Aho-Corasick

發布時間:2023/12/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 敏感词过滤算法Aho-Corasick 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

多模式串匹配算法簡介

敏感詞過濾最基本的原理就是字符串匹配算法,也就是通過維護一個敏感詞的字典,當用戶輸入一段文字內容后,通過字符串匹配算法,來查找用戶輸入的這段文字,是否包含敏感詞。

字符串匹配算法有很多比如BF算法、RK算法、BM算法、KMP算法還有Trie樹。前面四種算法都是單模式串匹配算法,只有Trie樹是多模式串匹配算法。

我們可以針對每個敏感詞,通過單模式匹配算法與用戶輸入的文字內容進行匹配。但是這樣做的話,每個需要匹配的敏感詞都需要掃描一遍用戶輸入的內容。如果敏感詞有很多,并且用戶輸入的內容很長,這種處理的方法就顯得比較低效。

與單模式匹配算法相比,多模式串匹配算法在敏感詞過濾這個問題上處理就很高效了,它只需要掃描一遍主串,就能在主串中一次性查找多個模式串是否存在。

Aho-Corasick算法

Aho-Corasick算法一般稱作AC自動機,AC自動機實際上就是在Trie樹之上,加了類似KMP的next數組,只不過此處的next數組是構建在trie樹上罷了。

AC自動機有三個核心函數,分別是:

  • success狀態,成功轉移到下一個節點(即Trie樹)
  • failure狀態,在該節點匹配失敗,則跳轉到一個特定的節點,從根節點到這個特定的節點的路徑恰好是失敗前文本的一部分。
  • output狀態,匹配到了敏感詞

根據以上AC算法的特點,改進Trie節點的屬性如下:

function TrieNode(key, parent, word) {this.key = key;this.children = []; this.parent = parent; // 該節點的父節點,用于構建failure表this.failure = null; // 失效之后指向的節點this.word = word // 該節點是否為某一個敏感詞的尾字符 }

構建Trie樹

和普通的trie構建是一樣的,逐個插入節點。假設敏感詞以及待過濾的字符串,均是小寫的英文字母。以英文字母的ASCII碼作為數組下標存儲節點。

function insert(data) {this.insertData(data, this.root); }function insertData(data, node){if (data === '') {return;}let children = node.children;let haveData = children[data[0].charCodeAt() - 97];if(haveData) {this.insertData(data.substring(1), haveData);}else{let isWord = data.length === 1;let insertNode = new TrieNode(data[0], node, isWord);children[data[0].charCodeAt() - 97] = insertNode;this.insertData(data.substring(1), insertNode); } }

添加Failure失效節點

下圖是以['HER', 'HEQ', 'SHR']構建的trie樹:

在這張圖中,虛線表示failure后的指向,上面我們也說到failure狀態的作用,就是在失配的時候告訴程序往哪里走,為什么要這么做,從這張表我們可以很清楚的看到,當我們匹配SHER時,程序會走右邊的分支,當走到S > H > E時,會出現失配,怎么辦?可能有小伙伴會想到回滾到ROOT從H開始重新匹配,但這樣回溯是有成本的,我們既然走了H節點,為什么要回溯呢?

這個時候failure就發揮作用了,我們看到右分支的H有一條虛線指向了左分支的H,我們也知道這就是failure的指向,通過這個指向,我們很輕松的將當前狀態移交過去。程序繼續匹配E > R,加上移交過來的H,我們可以輕松的匹配到HER。

問:假設有一個節點為currNode,它的子節點是childNode,那么子節點childNode的failure指向怎么求?

解:首先,我們需要找到childNode父節點currNode的failure指向,假設這個指向是Q的話,我們就要看看Q的孩子(children屬性)中有沒有與childNode字符相同(key相同)的節點,如果有的話,這個節點就是childNode的failure指向。如果沒有,我們就需要沿著currNode -> failure -> failure重復上述過程,如果一直沒找到,就將其指向root。

由此可知,一個節點的失效指針一定在該節點的上層。需要注意的是,我們在構建Trie樹時,并不知道failure指向到哪里的,所以failure指向需要在Trie樹構建完成后插入。

首先將trie樹第二層節點的失效指針指向root,之后逐層為每一個節點添加失效指針,即采用廣度優先遍歷Trie樹:

function getFailure() {let currQueue = Object.values(this.root.children);while (currQueue.length > 0) {let nextQueue = [];for (let i = 0; i < currQueue.length; i++) {let node = currQueue[i]let key = node.keylet parent = node.parentnode.failure = this.rootfor (let k in node.children) {nextQueue.push(node.children[k])}if (parent) {let failure = parent.failurewhile (failure) {let children = failure.children[key.charCodeAt() - 97]if (children) {node.failure = childrenbreak;}failure = failure.failure}}}currQueue = nextQueue} }

敏感詞過濾

對于Trie樹上的一些準備工作已經做完了,下面就是要對待匹配的字符串進行過濾。從頭遍歷當遇到output表中的節點時,就是出現了敏感詞。在匹配失敗的時候順著失效節點繼續匹配過程:

function filter(word) {let children = this.root.children;let currentNode = this.root;for(let i=0; i<word.length; i++){while(currentNode.children[word[i].charCodeAt() - 97] == null && currentNode != this.root) {currentNode = currentNode.failure;}currentNode = currentNode.children[word[i].charCodeAt() - 97];if (currentNode == null) {currentNode = this.root;}let temNode = currentNode;while(temNode != this.root) {if(temNode.word === true) {console.log('出現了敏感詞');} temNode = temNode.failure;}} }

測試

let trie = new Trie();// 生成trie樹 trie.insert('he'); trie.insert('his'); trie.insert('she'); trie.insert('hers');trie.getFailure();// 測試數據 trie.filter('ushers')// 該字符串出現了三個敏感詞

完整代碼

function TrieNode(key, parent, word) {this.key = key;this.children = [];this.parent = parent;this.failure = null;this.word = word }function Trie() {this.root = new TrieNode('/', null, false); // 添加根節點this.insert = insert; // 插入this.insertData = insertData;this.getFailure = getFailure;this.filter = filter; }function insert(data) {this.insertData(data, this.root); }function insertData(data, node){if (data === '') {return;}let children = node.children;let haveData = children[data[0].charCodeAt() - 97];if(haveData) {this.insertData(data.substring(1), haveData);}else{let isWord = data.length === 1;let insertNode = new TrieNode(data[0], node, isWord);children[data[0].charCodeAt() - 97] = insertNode;this.insertData(data.substring(1), insertNode); } }function getFailure() {let currQueue = Object.values(this.root.children);while (currQueue.length > 0) {let nextQueue = [];for (let i = 0; i < currQueue.length; i++) {let node = currQueue[i]let key = node.keylet parent = node.parentnode.failure = this.rootfor (let k in node.children) {nextQueue.push(node.children[k])}if (parent) {let failure = parent.failurewhile (failure) {let children = failure.children[key.charCodeAt() - 97]if (children) {node.failure = childrenbreak;}failure = failure.failure}}}currQueue = nextQueue} }function filter(word) {let children = this.root.children;let currentNode = this.root;for(let i=0; i<word.length; i++){while(currentNode.children[word[i].charCodeAt() - 97] == null && currentNode != this.root) {currentNode = currentNode.failure;}currentNode = currentNode.children[word[i].charCodeAt() - 97];if (currentNode == null) {currentNode = this.root;}let temNode = currentNode;while(temNode != this.root) {if(temNode.word === true) {console.log('出現了敏感詞');} temNode = temNode.failure;}} }let trie = new Trie();// 生成trie樹 trie.insert('he'); trie.insert('his'); trie.insert('she'); trie.insert('hers');trie.getFailure();// 測試數據 trie.filter('ushers')

總結

以上是生活随笔為你收集整理的敏感词过滤算法Aho-Corasick的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。