病毒侵襲持續(xù)中-AC自動(dòng)機(jī)

trie樹+kmp鞠绰。四瘫。fail指針其實(shí)就是相當(dāng)于kmp那個(gè)未優(yōu)化的next數(shù)組捶索,考慮到fail是有方向的八孝,方向可以理解成當(dāng)前這個(gè)(到這個(gè)節(jié)點(diǎn)為止)的后綴是之前一個(gè)短串的后綴董朝,是比root到當(dāng)前這個(gè)短的,所以在統(tǒng)計(jì)的時(shí)候干跛,才能保證fail往前走就可以子姜。
統(tǒng)計(jì)時(shí)候,fail數(shù)組一直往前,一路相加(這里設(shè)置了last數(shù)組哥捕,就是fail數(shù)組中節(jié)點(diǎn)是模式串結(jié)尾的點(diǎn))

插入:

void Insert(int v){
    int u = 0,len = strlen(ss[v]);
    for(int i = 0 ; i < len ; i++){
        int tmp = ss[v][i]-'A';
        if(!ch[u][tmp]){
            memset(ch[node],0,sizeof(ch[node]));
            val[node] = 0;
            ch[u][tmp] = node++;
        }
        u = ch[u][tmp];
    }
    val[u] = v;
}
  

fail:

fail指針指向的問題和KMP算法中構(gòu)造next數(shù)組的方式如出一轍牧抽。具體方法如下

1)將根結(jié)點(diǎn)的所有孩子結(jié)點(diǎn)的fail指向根結(jié)點(diǎn),然后將根結(jié)點(diǎn)的所有孩子結(jié)點(diǎn)依次入列遥赚。

2)若隊(duì)列不為空:

2.1)出列扬舒,我們將出列的結(jié)點(diǎn)記為curr, failTo表示curr的fail指向的結(jié)點(diǎn),即failTo = curr.fail

2.2) a.判斷curr.child[i] == failTo.child[i]是否成立凫佛,

       成立:curr.child[i].fail = failTo.child[i]讲坎,

       不成立:判斷 failTo == null是否成立

              成立: curr.child[i].fail == root

              不成立:執(zhí)行failTo = failTo.fail,繼續(xù)執(zhí)行2.2)

   b.curr.child[i]入列愧薛,再次執(zhí)行再次執(zhí)行步驟2)

若隊(duì)列為空:結(jié)束

//ch【】【】:第二維ascii碼范圍伺通,開260.
//last數(shù)組逢唤,就是fail數(shù)組中節(jié)點(diǎn)是模式串結(jié)尾的點(diǎn))
void  getfail(){
    queue<int> q;
    fail[0] = 0;
    for(int i = 0 ; i < ascs ; i++){
        if(ch[0][i]){  
            fail[ch[0][i]] = 0;
            q.push(ch[0][i]);
            last[ch[0][i]] = 0;
        }
    }

    while(!q.empty()){
        int tmp = q.front();
        q.pop();
        for(int i = 0 ; i < ascs ; i++){
            int u = ch[tmp][i];
            if(u != 0){
                q.push(u);
                int v = fail[tmp];
                while(v && ch[v][i]== 0) v = fail[v];
                fail[u] = ch[v][i];
                last[u] = val[fail[u]]?fail[u]:last[fail[u]];
            }
        }
    }
}

查詢:

void Find(){
    int len = strlen(s),j = 0;
    for(int i = 0 ; i < len ; i++){
       // if(s[i] <'A' ||s[i] > 'Z')  continue;
        int tmp = s[i]-'A';
        while(j && ch[j][tmp] == 0) j = fail[j];
        j = ch[j][tmp];

        if(val[j])  cal(j);
        else if(last[j])    cal(last[j]);
    }
}

Problem Description
小t非常感謝大家?guī)兔鉀Q了他的上一個(gè)問題。然而病毒侵襲持續(xù)中。在小t的不懈努力下航罗,他發(fā)現(xiàn)了網(wǎng)路中的“萬惡之源”靴寂。這是一個(gè)龐大的病毒網(wǎng)站塘揣,他有著好多好多的病毒辛掠,但是這個(gè)網(wǎng)站包含的病毒很奇怪,這些病毒的特征碼很短丰榴,而且只包含“英文大寫字符”货邓。當(dāng)然小t好想好想為民除害,但是小t從來不打沒有準(zhǔn)備的戰(zhàn)爭四濒。知己知彼换况,百戰(zhàn)不殆,小t首先要做的是知道這個(gè)病毒網(wǎng)站特征:包含多少不同的病毒盗蟆,每種病毒出現(xiàn)了多少次戈二。大家能再幫幫他嗎?

Input
第一行喳资,一個(gè)整數(shù)N(1<=N<=1000)觉吭,表示病毒特征碼的個(gè)數(shù)。
接下來N行仆邓,每行表示一個(gè)病毒特征碼鲜滩,特征碼字符串長度在1—50之間,并且只包含“英文大寫字符”节值。任意兩個(gè)病毒特征碼徙硅,不會(huì)完全相同。
在這之后一行搞疗,表示“萬惡之源”網(wǎng)站源碼嗓蘑,源碼字符串長度在2000000之內(nèi)。字符串中字符都是ASCII碼可見字符(不包括回車)。

Output
按以下格式每行一個(gè)桩皿,輸出每個(gè)病毒出現(xiàn)次數(shù)豌汇。未出現(xiàn)的病毒不需要輸出。
病毒特征碼: 出現(xiàn)次數(shù)
冒號(hào)后有一個(gè)空格泄隔,按病毒特征碼的輸入順序進(jìn)行輸出拒贱。

Sample Input
3
AA
BB
CC
ooxxCC%dAAAoen….END

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int n,node;
const int maxn = 50*1000+10;
const int ascs = 200;
const int mm = 2e6+10;
char s[mm],ss[1010][51];

int ch[maxn][ascs],fail[maxn],val[maxn],last[maxn],cnt[mm];
void Insert(int v){
    int u = 0,len = strlen(ss[v]);
    for(int i = 0 ; i < len ; i++){
        int tmp = ss[v][i]-'A';
        if(!ch[u][tmp]){
            memset(ch[node],0,sizeof(ch[node]));
            val[node] = 0;
            ch[u][tmp] = node++;
        }
        u = ch[u][tmp];
    }
    val[u] = v;
}

void  getfail(){
    queue<int> q;
    fail[0] = 0;
    for(int i = 0 ; i < ascs ; i++){
        if(ch[0][i]){
            fail[ch[0][i]] = 0;
            q.push(ch[0][i]);
            last[ch[0][i]] = 0;
        }
    }

    while(!q.empty()){
        int tmp = q.front();
        q.pop();
        for(int i = 0 ; i < ascs ; i++){
            int u = ch[tmp][i];
            if(u != 0){
                q.push(u);
                int v = fail[tmp];
                while(v && ch[v][i]== 0) v = fail[v];
                fail[u] = ch[v][i];
                last[u] = val[fail[u]]?fail[u]:last[fail[u]];
            }
        }
    }
}

void cal(int j){
    if(j){
        cnt[val[j]]++;
        cal(last[j]);
    }
}

void Find(){
    int len = strlen(s),j = 0;
    for(int i = 0 ; i < len ; i++){
       // if(s[i] <'A' ||s[i] > 'Z')  continue;
        int tmp = s[i]-'A';
        while(j && ch[j][tmp] == 0) j = fail[j];
        j = ch[j][tmp];

        if(val[j])  cal(j);
        else if(last[j])    cal(last[j]);
    }
}

void init(){
    node = 1;
    memset(ch[0],0,sizeof(ch[0]));
    memset(cnt, 0, sizeof(cnt[0])*(n+2));
    for(int i = 1 ;i <= n ; i++){
        scanf("%s",ss[i]);
        //cout << ss[i]<<endl;
        Insert(i);
    }
    getfail();
}

void sov(){
    scanf("%s",s);
    Find();
    for(int i = 1; i <= n ; i ++){
        if(cnt[i] == 0) continue;
        printf("%s: %d\n",ss[i],cnt[i]);
    }
}

int main(){
    while(~scanf("%d",&n)){
       init();
       sov();
    }

    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市佛嬉,隨后出現(xiàn)的幾起案子柜思,更是在濱河造成了極大的恐慌,老刑警劉巖巷燥,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異号枕,居然都是意外死亡缰揪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門葱淳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钝腺,“玉大人,你說我怎么就攤上這事赞厕⊙藓” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵皿桑,是天一觀的道長毫目。 經(jīng)常有香客問我,道長诲侮,這世上最難降的妖魔是什么镀虐? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮沟绪,結(jié)果婚禮上刮便,老公的妹妹穿的比我還像新娘。我一直安慰自己绽慈,他們只是感情好恨旱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坝疼,像睡著了一般搜贤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裙士,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天入客,我揣著相機(jī)與錄音,去河邊找鬼。 笑死桌硫,一個(gè)胖子當(dāng)著我的面吹牛夭咬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铆隘,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卓舵,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了膀钠?” 一聲冷哼從身側(cè)響起掏湾,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肿嘲,沒想到半個(gè)月后融击,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雳窟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年尊浪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片封救。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拇涤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出誉结,到底是詐尸還是另有隱情鹅士,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布惩坑,位于F島的核電站掉盅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旭贬。R本人自食惡果不足惜怔接,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稀轨。 院中可真熱鬧扼脐,春花似錦、人聲如沸奋刽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佣谐。三九已至肚吏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狭魂,已是汗流浹背罚攀。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工党觅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斋泄。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓杯瞻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炫掐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子魁莉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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

  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系,并對(duì)這種結(jié)構(gòu)定義相應(yīng)的運(yùn)算募胃,而且確保經(jīng)過這...
    Winterfell_Z閱讀 5,787評(píng)論 0 13
  • 1)這本書為什么值得看: Python語言描述旗唁,如果學(xué)的Python用這本書學(xué)數(shù)據(jù)結(jié)構(gòu)更合適 2016年出版,內(nèi)容...
    孫懷闊閱讀 12,466評(píng)論 0 15
  • 預(yù)備知識(shí) Trie(字典樹)KMP字符串匹配算法 AC自動(dòng)機(jī)求解問題的類型 一句話概括就是:多模匹配痹束。KMP求解的...
    ZJL_OIJR閱讀 1,007評(píng)論 0 1
  • 1检疫、 鐵人三項(xiàng) 每組5人參賽,共1組祷嘶,采取一局一勝賽制电谣。第一個(gè)項(xiàng)目開始后即開始計(jì)時(shí),第一個(gè)項(xiàng)目為氣球運(yùn)輸抹蚀,每組2人...
    蘇州暮雨閱讀 389評(píng)論 0 0
  • 孩子正式放寒假了,我們要去爸爸單位住企垦,剛好今天有時(shí)間环壤,就帶他去他同學(xué)家玩玩,可是四個(gè)孩子真的好鬧騰俺睢郑现!孩...
    愛你的貝貝閱讀 352評(píng)論 0 2