1. 程式人生 > >2896 病毒侵襲(AC自動機模板)

2896 病毒侵襲(AC自動機模板)

病毒侵襲

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 33670    Accepted Submission(s): 7578
Problem Description當太陽的光輝逐漸被月亮遮蔽,世界失去了光明,大地迎來最黑暗的時刻。。。。在這樣的時刻,人們卻異常興奮——我們能在有生之年看到500年一遇的世界奇觀,那是多麼幸福的事兒啊~~
但網路上總有那麼些網站,開始藉著民眾的好奇心,打著介紹日食的旗號,大肆傳播病毒。小t不幸成為受害者之一。小t如此生氣,他決定要把世界上所有帶病毒的網站都找出來。當然,誰都知道這是不可能的。小t卻執意要完成這不能的任務,他說:“子子孫孫無窮匱也!”(愚公後繼有人了)。
萬事開頭難,小t收集了好多病毒的特徵碼,又收集了一批詭異網站的原始碼,他想知道這些網站中哪些是有病毒的,又是帶了怎樣的病毒呢?順便還想知道他到底收集了多少帶病毒的網站。這時候他卻不知道何從下手了。所以想請大家幫幫忙。小t又是個急性子哦,所以解決問題越快越好哦~~Input
第一行,一個整數N(1<=N<=500),表示病毒特徵碼的個數。
接下來N行,每行表示一個病毒特徵碼,特徵碼字串長度在20—200之間。
每個病毒都有一個編號,依此為1—N。
不同編號的病毒特徵碼不會相同。
在這之後一行,有一個整數M(1<=M<=1000),表示網站數。
接下來M行,每行表示一個網站原始碼,原始碼字串長度在7000—10000之間。
每個網站都有一個編號,依此為1—M。
以上字串中字元都是ASCII碼可見字元(不包括回車)。Output依次按如下格式輸出按網站編號從小到大輸出,帶病毒的網站編號和包含病毒編號,每行一個含毒網站資訊。
web 網站編號: 病毒編號 病毒編號 …
冒號後有一個空格,病毒編號按從小到大排列,兩個病毒編號之間用一個空格隔開,如果一個網站包含病毒,病毒數不會超過3個。
最後一行輸出統計資訊,如下格式
total: 帶病毒網站數
冒號後有一個空格。
Sample Input
3 aaa bbb ccc 2 aaabbbccc bbaaccSample Outputweb 1: 1 2 3 total: 1
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
struct Trie
{
    int next[210*500][130], fail[210*500], end[210*500];//一維下標代表節點數  end代表了節點數,除葉子節點以外全部是-1.else是字串的ID
    //next的值代表子節點的編號
    int root ,L;//L代表節點總數
    int Newnode()
    {
        for(int i=0; i<130; i++)
            next[L][i] = -1;
        end[L++] = -1;//除了葉子節點以外全部設為-1;
        return L-1;
    }
    void Init()
    {
        L = 0;
        root = Newnode();
    }
    void Insert(char *s, int id)
    {
        int len = strlen(s);
        int now = root;
        for(int i=0; i<len; i++)
        {
            if(next[now][s[i]] == -1)
                next[now][s[i]] = Newnode();
            now = next[now][s[i]];//往下形成一條鏈
        }
        end[now] = id;//葉子節點設定為該字串的編號.
    }
    void Build()
    {
        queue <int> Q;
        fail[root] = root;
        for(int i=0 ; i<130; i++)
        {
            if(next[root][i] == -1)
                next[root][i] = root;//如果根節點的子節點為空,那麼直接賦值為root
                //這樣也起到了一個類似失配指標的作用,如果 是NULL,也是失配的情況,為了提高搜尋效率也應該轉到最長字尾的節點。
            else
            {
                fail[next[root][i]] = root;//第二層的節點全部反指向根節點
                Q.push(next[root][i]);//第二層節點入隊
            }
        }
        while(!Q.empty())
        {
            int now = Q.front();//深搜的基本操作了,以當前節點為中心搜尋所有子節點入隊
            Q.pop();
            for(int i=0; i<130; i++)
            {
                if(next[now][i] == -1)//
                    next[now][i] = next[fail[now]][i];//如果節點的子節點為空,那麼直接賦值為其失配指標指向的節點的子節點
                    //這樣也起到了一個類似失配指標的作用,如果 是NULL,也是失配的情況,為了提高搜尋效率也應該轉到最長字尾的節點。
                    //他喵的這難道不是個圖了嗎???
                else
                {
                    fail[next[now][i]] = next[fail[now]][i];//因為now節點作為父節點一定已經匹配了失配指標
                    //所以now節點的子節點的失配指標直接指向now節點的失配指標所指向的節點的子節點。
                    //now節點的父節點和 now指向的節點的父節點一定相等,而now和 now的子節點指向的節點的父節點一定相等,
                    //這樣形成了一個節點所指向的節點,其兩者的父節點一定相等,這樣跳轉提高了搜尋效率。
                    Q.push(next[now][i]);
                }

            }
        }
    }
    bool used[510];
    bool Query(char *buf, int n, int id)
    {
        int len = strlen(buf);
        int now = root;
        memset(used, false, sizeof(used));
        bool flag = false;
        for(int i=0; i<len; i++)
        {
            now = next[now][buf[i]];
            int tmp = now;//now要記錄當前字元的節點位置所以不能動
            while(tmp != root)//這裡一直在搜尋葉子節點(即以0-i為字首(這個想法感覺比較重要)的所有可能的字串)
            {
                if(end[tmp] != -1)// !=-1說明已經到了葉子節點
                {
                    used[end[tmp]] = true;
                    flag = true;
                }
                tmp = fail[tmp];
            }
        }
        if(!flag)
            return false;
        printf("web %d:", id);
        for(int i=1; i<=n; i++)
            if(used[i])
                printf(" %d",i);
        puts("");
        return true;
    }
} AC;
char buf[10010];
int main()
{
    int n,m;
    while(~scanf("%d",&n))
    {
        AC.Init();
        for(int i=1; i<=n; i++)
        {
            scanf("%s",buf);
            AC.Insert(buf,i);
        }
        AC.Build();
        /*for (int i = 0;i < 20; i++){
			cout << AC.end[i] << ' ';
        }
        cout << '\n';*/
        int ans = 0;
        scanf("%d",&m);
        for(int i=1; i<=m; i++)
        {
            scanf("%s",buf);
            if(AC.Query(buf, n, i))
                ans++;
        }
        printf("total: %d\n", ans);
    }
    return 0;
}

相關推薦

2896 病毒侵襲(AC自動機模板)

病毒侵襲Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 33670    Accepted Submission(s

HDU-2896 病毒侵襲(AC自動機)

所有 build 我們 main init ane 哪些 nod class 病毒侵襲 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Sub

hdu 2896 病毒侵襲 AC自動機

病毒侵襲 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 20142    Accepted Submissio

HDU 2896 病毒入侵 AC自動機

病毒侵襲 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 37537  

HDU2896 病毒侵襲 AC自動機

題目大意:給出n個病毒和n個網站,找出每一個網站中含有的病毒種類,並按病毒編號升序輸出,最後統計含有病毒的網站個數。 分析:比較裸的AC自動機的題,我們可以在構造tire樹的過程中順便把編號插入,然後詢問時紀錄病毒個數的同時用一個數組紀錄病毒的編號,然後排序輸出即可(WA

HDU 2896 病毒侵襲

http app != define int public front std lag 二次聯通門 : HDU 2896 病毒侵襲 /* HDU 2896 病毒侵襲 AC自動機 此題太坑啦。。。 用

AC自動機模板

insert 自動機 mat roo oot node truct amp 字典樹 struct node { node *next[26]; node *fail; int sum; }; int cnt; node *roo

HDU3695(AC自動機模板題)

16px tdi out 模板題 getc else return typedef queue   題意:給你n個字符串,再給你一個大的字符串A,問你著n個字符串在正的A和反的A裏出現多少個?   其實就是AC自動機模板題啊( ╯□╰ )   正著query一次再反著que

hdu 2896 病毒侵襲

search ash names accep amp mis null 包含 aaa Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi

hdu2222(ac自動機模板)

har isp truct itl color 樹節點 namespace print 好的 先推薦兩篇寫的很好的ac自動機blog: http://blog.csdn.net/creatorx/article/details/71100840 http://blog.cs

HDU 2222 Keywords Search(AC自動機模板題)

stack uil empty cst keywords cto ble ont max http://acm.hdu.edu.cn/showproblem.php?pid=2222 題意:給出多個單詞,最後再給出一個模式串,求在該模式串中包含了多少個單詞。 思路

HDU 2222 Keywords Search (AC自動機模板題)

出現 tro spa 繼續 time int cas keyword arc Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Othe

BZOJ2938 [Poi2000]病毒AC自動機

格式 指向 namespace ++ getch continue c++ sca 存在 題目 二進制病毒審查委員會最近發現了如下的規律:某些確定的二進制串是病毒的代碼。如果某段代碼中不存在任何一段病毒代碼,那麽我們就稱這段代碼是安全的。現在委員會已經找出了所有的病毒代碼段

2938. [POI2000]病毒AC自動機

color esc mar hellip tex dash sample script 需要 Description 二進制病毒審查委員會最近發現了如下的規律:某些確定的二進制串是病毒的代碼。如果某段代碼中不存在任何一段病毒代碼,那麽我們就稱這段代碼是安全的。現在委員

洛谷P3808 & P3796 AC自動機模板

style sca %d www. ios https front query 字符 題目:P3808:https://www.luogu.org/problemnew/show/P3808 P3796:https://www.luogu.org/problemnew/sh

hdu 2222 AC自動機(模板題)

模式串 har sizeof 問題 null word 指向 pan 子節點 <題目鏈接> 題目大意: 給你一些單詞,和一個字符串,問你這個字符串中含有多少個上面的單詞。 解題分析: 這是多模匹配問題,如果用KMP的話,對每一個單詞,都跑一遍KMP,那麽當單詞數

Keywords Search HDU2222 AC自動機模板

type pop class 都是 head gcd rds cst ons ac自動機說起來很復雜,其實和kmp是一樣的思路,都是尋找相同前後綴,減少跳的次數。只要理解了kmp是怎麽求next數組的,ac自動機bfs甚至比knp還好寫。 這裏大致說一下kmp求next數組

洛谷P2444 病毒AC自動機

long 字符 結果 程序 沒有 int pan 描述 恢復 題目描述 二進制病毒審查委員會最近發現了如下的規律:某些確定的二進制串是病毒的代碼。如果某段代碼中不存在任何一段病毒代碼,那麽我們就稱這段代碼是安全的。現在委員會已經找出了所有的病毒代碼段,試問,是否存在一個無限

18.10.29 躲不開的病毒AC自動機+dfs)

ac自動機 class 都是 輸出 insert == char 特征 math.h 描述 有若幹種病毒,每種病毒的特征代碼都是一個01串。 每個程序也都是一個01串。 問是否存在不被病毒感染(不包含任何病毒的特征代碼)的無限長的程序。 輸入第一行是整數n,表示有n

洛谷2444 [POI2000]病毒AC自動機)(DFS)

題目 二進位制病毒審查委員會最近發現瞭如下的規律:某些確定的二進位制串是病毒的程式碼。如果某段程式碼中不存在任何一段病毒程式碼,那麼我們就稱這段程式碼是安全的。現在委員會已經找出了所有的病毒程式碼段,試問,是否存在一個無限長的安全的二進位制程式碼。 示例: 例如如果{011, 11, 0