1. 程式人生 > >【HDU 2896】病毒侵襲(AC自動機-用陣列完成)

【HDU 2896】病毒侵襲(AC自動機-用陣列完成)

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 bbaacc

Sample  Output:

web 1: 1 2 3 total: 1

思路:這道題是學姐從杭電拉出來的,用連結串列提交,wa了(記憶體超限),在外面提交的時候過了,但是發現他的記憶體是擦邊兒卡過去的,在學姐拉的題裡面就擦不了邊兒了,必須把連結串列改成陣列去實現AC自動機,才能AC。而且,我感覺還是陣列方便,連結串列還得開闢空間,還得釋放記憶體的,到最後還記憶體超限,還不如陣列來得實在。其實程式碼都一樣,只是把連結串列部分換成了陣列。

My  DaiMa:

#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
#include<malloc.h>
#include<algorithm>
using namespace std;
const int allson = 130;///此題中字元是ASCII表中所有字元
char pattern[205];
char text[10005];
int node[100005][allson];///節點
int fail[100005];///失敗指標
int num[100005];///用來存放某個節點的單詞數
int id;
int vis[505];///用來標記病毒是否已經出現過
/*初始化模式串,類似於構建字典樹*/
void insertPattern(int i)
{
    int p = 0;
    int index = 0;
    while(pattern[index] != '\0')
    {
        int son = pattern[index];
        if(node[p][son] == -1)
        {
            memset(node[id],-1,sizeof(node[id]));
            num[id] = 0;
            node[p][son] = id++;///node用來存放編號
        }
        p = node[p][son];
        index++;
    }
    num[p] = i;///將單詞的末尾字元的num定義為單詞的序號,便於後期輸出病毒序號

}
/*構建AC自動機,計算fail指標*/
void build_AC_automaton()
{
    int p = 0;
    queue<int> qu;
    qu.push(p);
    while(!qu.empty())
    {
        p = qu.front();
        qu.pop();
        for(int i = 0; i < allson; i++)
        {
            if(node[p][i] != -1)
            {
                if(p == 0)///如果是根節點的兒子,它們的fail指標指向根
                    fail[node[p][i]] = 0;
                else
                {
                    int temp = fail[p];
                    while(temp != -1)
                    {
                        if(node[temp][i] != -1)///如果在它的父親的fail指標下的兒子中能找到跟它相同的字元
                        {                      ///就讓它的fail指標指向此節點
                            fail[node[p][i]] = node[temp][i];
                            break;
                        }
                         temp = fail[temp];
                    }
                    if(temp == -1)///如果找不到,就讓它的fail指標指向根
                        fail[node[p][i]] = 0;
                }
                qu.push(node[p][i]);
            }
        }
    }
}
/*遍歷AC自動機,匹配文字串*/
int find_in_AC_automaton()
{
    int p = 0;
    int index = 0;
    int k = 0;
    while(text[index] != '\0')
    {
        int son = text[index];
        while(node[p][son] == -1 && p != 0)///失敗的時候返回它的fail地方再嘗試進行匹配
            p = fail[p];
        p = node[p][son];
        if(p == -1) p = 0;
        int temp = p;
        while(temp != 0 && num[temp] != 0 && vis[num[temp]] == 0)
        {
            a[k++] = num[temp];///記錄出現的病毒的序號
            vis[num[temp]] = 1;///將出現過的病毒標記掉
            temp = fail[temp];
        }
        index++;
        if(k >= 3)
            break;
    }
    return k;
}
int main()
{
    int n,m;
    fail[0] = -1;
    memset(node[0],-1,sizeof(node[0]));
    num[0] = 0;
    id = 1;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%s",pattern);
        insertPattern(i);
    }
    build_AC_automaton();
    scanf("%d",&m);
    int total = 0;
    for(int i = 1; i <= m; i++)
    {
        scanf("%s",text);
        memset(vis,0,sizeof(vis));
        int flag = find_in_AC_automaton();
        if(flag != 0)
        {
            total++;
            printf("web %d:",i);
            sort(a,a+flag);///將病毒序號進行從小到大排序
            for(int j = 0; j < flag; j++)
                printf(" %d",a[j]);
            printf("\n");
        }
    }
    printf("total: %d\n",total);
}