hdu 2222 AC自動機靜態模板

題意:給出多個模式串,看是否在文本串裏出現


思路:ac自動機第一題,參考的是lrj的靜態寫法,但因爲涉及到重複模式串的問題,要進行一點改動,感謝hdu discuss裏提供給的兩組數據

/*

1

6

she

he

say

shr

her

yasherhs

*/

/*

1

3

she

she

she

shesheshe

*/

第一組樣例答案爲4,第二組樣例答案爲3

下面會在模板加註釋說明


題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=2222



#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int maxnode = 1000005;
const int nodesize = 26;

struct Aho_corasick
{
    int ch[maxnode][nodesize];
    int val[maxnode];
    int f[maxnode];
    int last[maxnode];
    bool vis[maxnode];
    int sz;
    int ans;

    void init()
    {
        sz = 1;
        memset(vis, false, sizeof(vis));
        memset(ch[0], 0, sizeof(ch[0]));
        memset(val, 0, sizeof(val));
    }

    int id(char c)
    {
        return c - 'a';
    }

    void Insert(const char *s)
    {
        int u = 0;
        int len = strlen(s);
        for(int i = 0; i < len; i++)
        {
            int c = id(s[i]);
            if(!ch[u][c])
            {
                memset(ch[sz], 0, sizeof(ch[sz]));
                ch[u][c] = sz++;//這裏不能加上val[u] = 0,因爲有可能有的模式串是該模式串的前綴
            }
            u = ch[u][c];
        }
        val[u]++;
    }

    void getFail()
    {
        queue<int> q;
        for(int c = 0; c < nodesize; c++)
        {
            int u = ch[0][c];
            if(u)
            {
                f[u] = 0;
                q.push(u);
                last[u] = 0;
            }
        }

        while(!q.empty())
        {
            int r = q.front();
            q.pop();
            for(int c = 0; c < nodesize; c++)
            {
                int u = ch[r][c];
                if(!u) continue;
                q.push(u);
                int v = f[r];
                while(v && !ch[v][c]) v = f[v];
                f[u] = ch[v][c];
                last[u] = val[f[u]] ? f[u] : last[f[u]];
            }
        }
    }

    int getans(int j)
    {
        if(j && vis[j] == false)//vis數組的作用是對於第二組樣例的,如果該模式串已經被用過,那麼下次再在文本串匹配到不應再用
        {
            vis[j] = true;//j能進到這個說明存在j這個節點,要繼續找它的後綴鏈接,但它不一定是單詞節點,所以val[j]可能爲0
            return getans(last[j]) + val[j];
        }
        else return 0;
    }

    void Find(const char *s)
    {
        ans = 0;
        int len = strlen(s);
        int j = 0;
        for(int i = 0; i < len; i++)
        {
            int c = id(s[i]);
            while(j && !ch[j][c]) j = f[j];
            j = ch[j][c];
            if(val[j]) ans += getans(j);//說明此時j爲單詞節點,即trie根節點編號,所以匹配成功
            else if(last[j]) ans += getans(j);//此時j不是單詞節點,但也找到它的後綴鏈接
        }
    }
};

Aho_corasick ac;
char T[1000005];

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        ac.init();
        char s[60];
        int n;
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
        {
            scanf("%s", s);
            ac.Insert(s);
        }
        ac.getFail();
        scanf("%s", T);
        ac.Find(T);
        printf("%d\n", ac.ans);
    }
    return 0 ;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章