數(shù)據(jù)結(jié)構(gòu)與算法Day29----字符串匹配(五):AC自動(dòng)機(jī)

一窥淆、如何實(shí)現(xiàn)敏感詞過(guò)濾:

1、基于單模式串實(shí)現(xiàn)的敏感詞過(guò)濾:

<1>滤灯、單模式串匹配算法:

??是在一個(gè)模式串和一個(gè)主串之間進(jìn)行匹配坪稽,也就是說(shuō),在一個(gè)主串中查找一個(gè)模式串鳞骤。BF算法窒百、 RK算法、 BM算法豫尽、 KMP算法都是單模式匹配算法篙梢。

<2>、基于單模式串實(shí)現(xiàn)敏感詞過(guò)濾的方法:

??針對(duì)每個(gè)敏感詞拂募,通過(guò)單模式串匹配算法(比如KMP算法)與用戶輸入的文字內(nèi)容進(jìn)行匹配庭猩。但是,這樣做的話陈症,每個(gè)匹配過(guò)程都需要掃描一遍用戶輸入的內(nèi)容蔼水。整個(gè)過(guò)程下來(lái)就要掃描很多遍用戶輸入的內(nèi)容。如果敏感詞很多录肯,比如幾千個(gè)趴腋,并且用戶輸入的內(nèi)容很長(zhǎng),假如有上千個(gè)字符论咏,那我們就需要掃描幾千遍這樣的輸入內(nèi)容优炬。很顯然,這種處理思路比較低效厅贪。

2蠢护、基于Trie樹實(shí)現(xiàn)的敏感詞過(guò)濾:

<1>、多模式串匹配算法:

??是在多個(gè)模式串和一個(gè)主串之間做匹配养涮,也就是說(shuō)葵硕,在一個(gè)主串中查找多個(gè)模式串。Trie樹是多模式匹配算法贯吓。

<2>懈凹、基于Trie樹實(shí)現(xiàn)敏感詞過(guò)濾的方法:

??對(duì)敏感詞字典進(jìn)行預(yù)處理,構(gòu)建成Trie樹結(jié)構(gòu)悄谐。這個(gè)預(yù)處理的操作只需要做一次介评,如果敏感詞字典動(dòng)態(tài)更新了,比如刪除爬舰、添加了一個(gè)敏感詞们陆,只需要?jiǎng)討B(tài)更新一下Trie樹即可寒瓦。當(dāng)用戶輸入一個(gè)文本內(nèi)容后,把用戶輸入的內(nèi)容作為主串棒掠,從第一個(gè)字符(假設(shè)是字符C)開始孵构,在Trie樹中匹配。當(dāng)匹配到Trie樹的葉子節(jié)點(diǎn)烟很,或者中途遇到不匹配字符的時(shí)候,將主串的開始匹配位置后移一位蜡镶,也就是從字符C的下一個(gè)字符開始雾袱,重新在Trie樹中匹配。

二官还、AC自動(dòng)機(jī):

1芹橡、AC自動(dòng)機(jī)概念:

??AC自動(dòng)機(jī)算法,全稱是Aho-Corasick算法望伦。其實(shí)林说, Trie樹跟AC自動(dòng)機(jī)之間的關(guān)系,就像單串匹配中樸素的串匹配算法屯伞,跟KMP算法之間的關(guān)系一樣腿箩,只不過(guò)前者針對(duì)的是多模式串而已。所以劣摇, AC自動(dòng)機(jī)實(shí)際上就是在Trie樹之上珠移,加了類似KMP的next數(shù)組,只不過(guò)此處的next數(shù)組是構(gòu)建在樹上罷了末融。

2钧惧、AC自動(dòng)機(jī)的代碼表示:

public class AcNode {
    public char data;
    public AcNode[] children = new AcNode[26]; // 字符集只包含a~z這26個(gè)字符
    public boolean isEndingChar = false; // 結(jié)尾字符為true
    public int length = -1; // 當(dāng)isEndingChar=true時(shí),記錄模式串長(zhǎng)度
    public AcNode fail; // 失敗指針
    public AcNode(char data) {
        this.data = data;
    }
}

3勾习、AC自動(dòng)機(jī)的構(gòu)建操作:

  • 將多個(gè)模式串構(gòu)建成Trie樹浓瞪;
  • 在Trie樹上構(gòu)建失敗指針(相當(dāng)于KMP中的失效函數(shù)next數(shù)組)。

4巧婶、AC自動(dòng)機(jī)構(gòu)建失敗指針的方法:

<1>乾颁、構(gòu)建思路:

假設(shè)這里有4個(gè)模式串,分別是c粹舵, bc钮孵, bcd, abcd眼滤;主串是abcd巴席。


??Trie樹中的每一個(gè)節(jié)點(diǎn)都有一個(gè)失敗指針,它的作用和構(gòu)建過(guò)程诅需,跟KMP算法中的next數(shù)組極其相似漾唉。
??假設(shè)沿Trie樹走到p節(jié)點(diǎn)荧库,也就是下圖中的紫色節(jié)點(diǎn),那p的失敗指針就是從root走到紫色節(jié)點(diǎn)形成的字符串a(chǎn)bc赵刑,跟所有模式串前綴匹配的最長(zhǎng)可匹配后綴子串分衫,就是箭頭指的bc模式串。
??最長(zhǎng)可匹配后綴子串:字符串a(chǎn)bc的后綴子串有兩個(gè)bc般此, c蚪战,拿它們與其他模式串匹配,如果某個(gè)后綴子串可以匹配某個(gè)模式串的前綴铐懊,那就把這個(gè)后綴子串叫作可匹配后綴子串邀桑。從可匹配后綴子串中,找出最長(zhǎng)的一個(gè)科乎,就是剛剛講到的最長(zhǎng)可匹配后綴子串壁畸。將p節(jié)點(diǎn)的失敗指針指向那個(gè)最長(zhǎng)匹配后綴子串對(duì)應(yīng)的模式串的前綴的最后一個(gè)節(jié)點(diǎn),就是下圖中箭頭指向的節(jié)點(diǎn)茅茂。

??當(dāng)要求某個(gè)節(jié)點(diǎn)的失敗指針的時(shí)候捏萍,通過(guò)已經(jīng)求得的、深度更小的那些節(jié)點(diǎn)的失敗指針來(lái)推導(dǎo)空闲。也就是說(shuō)令杈,可以逐層依次來(lái)求解每個(gè)節(jié)點(diǎn)的失敗指針。所以进副,失敗指針的構(gòu)建過(guò)程这揣,是一個(gè)按層遍歷樹的過(guò)程。
??首先root的失敗指針為NULL影斑,也就是指向自己给赞。 當(dāng)已經(jīng)求得某個(gè)節(jié)點(diǎn)p的失敗指針之后,假設(shè)節(jié)點(diǎn)p的失敗指針指向節(jié)點(diǎn)q矫户,看節(jié)點(diǎn)p的子節(jié)點(diǎn)pc對(duì)應(yīng)的字符片迅,是否也可以在節(jié)點(diǎn)q的子節(jié)點(diǎn)中找到。如果找到了節(jié)點(diǎn)q的一個(gè)子節(jié)點(diǎn)qc皆辽,對(duì)應(yīng)的字符跟節(jié)點(diǎn)pc對(duì)應(yīng)的字符相同柑蛇,則將節(jié)點(diǎn)pc的失敗指針指向節(jié)點(diǎn)qc。

??如果節(jié)點(diǎn)q中沒(méi)有子節(jié)點(diǎn)的字符等于節(jié)點(diǎn)pc包含的字符驱闷,則令q=q->fail(fail表示失敗指針耻台,這里有沒(méi)有很像KMP算法里求next的過(guò)程?)空另,繼續(xù)上面的查找盆耽,直到q是root為止,如果還沒(méi)有找到相同字符的子節(jié)點(diǎn),就讓節(jié)點(diǎn)pc的失敗指針指向root摄杂。

??最終AC自動(dòng)機(jī)構(gòu)建如下:

<2>坝咐、構(gòu)建代碼:

public void buildFailurePointer() {
    Queue<AcNode> queue = new LinkedList<>();
    root.fail = null;
    queue.add(root);
    while (!queue.isEmpty()) {
        AcNode p = queue.remove();
        for (int i = 0; i < 26; ++i) {
            AcNode pc = p.children[i];
            if (pc == null) continue;
            if (p == root) {
                pc.fail = root;
            } else {
                AcNode q = p.fail;
                while (q != null) {
                    AcNode qc = q.children[pc.data - 'a'];
                    if (qc != null) {
                        pc.fail = qc;
                        break;
                    }
                    q = q.fail;
                }
                if (q == null) {
                    pc.fail = root;
                }
            }
            queue.add(pc);
        }
    }
}

5、在AC自動(dòng)機(jī)上匹配主串的方法:

<1>析恢、原理:

??在匹配過(guò)程中墨坚,主串從i=0開始, AC自動(dòng)機(jī)從指針p=root開始映挂,假設(shè)模式串是b泽篮,主串是a。

  • 如果p指向的節(jié)點(diǎn)有一個(gè)等于b[i]的子節(jié)點(diǎn)x袖肥,就更新p指向x咪辱,這個(gè)時(shí)候需要通過(guò)失敗指針,檢測(cè)一系列失敗指針為結(jié)尾的路徑是否是模式串椎组。處理完之后,將i加一历恐,繼續(xù)這兩個(gè)過(guò)程寸癌;
  • 如果p指向的節(jié)點(diǎn)沒(méi)有等于b[i]的子節(jié)點(diǎn),讓p=p->fail弱贼,然后繼續(xù)這兩個(gè)過(guò)程蒸苇。

<2>、代碼:

public void match(char[] text) { // text是主串
    int n = text.length;
    AcNode p = root;
    for (int i = 0; i < n; ++i) {
        int idx = text[i] - 'a';
        while (p.children[idx] == null && p != root) {
            p = p.fail; // 失敗指針發(fā)揮作用的地方
        }
        p = p.children[idx];
        if (p == null) p = root; // 如果沒(méi)有匹配的吮旅,從root開始重新匹配
        AcNode tmp = p;
        while (tmp != root) { // 打印出可以匹配的模式串
            if (tmp.isEndingChar == true) {
                int pos = i-tmp.length+1;
                System.out.println("匹配起始下標(biāo)" + pos + "; 長(zhǎng)度" + tmp.length);
            }
            tmp = tmp.fail;
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溪烤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子庇勃,更是在濱河造成了極大的恐慌檬嘀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件责嚷,死亡現(xiàn)場(chǎng)離奇詭異鸳兽,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)罕拂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門揍异,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人爆班,你說(shuō)我怎么就攤上這事衷掷。” “怎么了柿菩?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵啊楚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我津辩,道長(zhǎng),這世上最難降的妖魔是什么镜悉? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮医瘫,結(jié)果婚禮上侣肄,老公的妹妹穿的比我還像新娘。我一直安慰自己醇份,他們只是感情好稼锅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著僚纷,像睡著了一般矩距。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怖竭,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天锥债,我揣著相機(jī)與錄音,去河邊找鬼痊臭。 笑死哮肚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的广匙。 我是一名探鬼主播允趟,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鸦致!你這毒婦竟也來(lái)了潮剪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤分唾,失蹤者是張志新(化名)和其女友劉穎抗碰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳍寂,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡改含,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迄汛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捍壤。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞍爱,靈堂內(nèi)的尸體忽然破棺而出鹃觉,到底是詐尸還是另有隱情,我是刑警寧澤睹逃,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布盗扇,位于F島的核電站祷肯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏疗隶。R本人自食惡果不足惜佑笋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斑鼻。 院中可真熱鬧蒋纬,春花似錦、人聲如沸坚弱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)荒叶。三九已至碾阁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間些楣,已是汗流浹背脂凶。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愁茁,地道東北人艰猬。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像埋市,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子命贴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容