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

題目鏈接

問題描述:給你n個模式串,再給你一個目標串,問n個模式串中有多少串存在在目標串中。

思路:AC自動機模板題

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<time.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>

#define pi acos(-1)
#define maxn 111111
#define maxm 1111111
#define INF 0x3F3F3F3F
#define eps 1e-8

#define pb push_back

#define mem(a) memset(a,0,sizeof a)

using namespace std;

const long long mod = 1000000007;
/**lyc**/
char str[maxn][77];   ///模式串
int n;
char a[maxm];

#define STAUTS_NUM 500000
struct trie {
    ///next,fail,end都是基於第幾個狀態 狀態就是一個字符串順序
    int next[STAUTS_NUM][26], ///next數組表示 next[i][j]表示第i個狀態後連j字符會變成什麼狀態,
                        ///注意最開始初始化的狀態,在後面會被不斷的優化
    fail[STAUTS_NUM],         ///失配數組,表示應該回到一個之前已匹配部分的最長後綴那裏開始重新匹配。類似kmp
    end[STAUTS_NUM];          ///實際上就是表示哪些序列是模式串中有的,1,0,表示
  //  int next2[maxn][26]; /**這個數組是我這個程序中另外設的,用來儲存最原始的next數組,有特殊用**/
    int num[maxn];///每個單詞出現的次數
    int f[STAUTS_NUM];///每個節點對應的單詞編號

    int root,  ///根節點狀態,默認是0
    cnt;       ///狀態數
    int new_node () {  ///對於一個沒有的狀態,對他新建一個節點
        memset (next[cnt], -1, sizeof next[cnt]);
        end[cnt++] = 0;
        return cnt-1;
    }
    void init () {    ///初始化這棵字典樹
        cnt = 0;
        root = new_node ();
        memset (num, 0, sizeof num);
        memset (f, -1, sizeof f);
    }
    void insert (char *buf, int pos) { ///字典樹插入一個模式串
        int len = strlen (buf);
        int now = root;
        for (int i = 0; i < len; i++) {
            int id = buf[i] - 'a';
            if (next[now][id] == -1) {  ///沒有這個狀態就新建一個節點
                next[now][id] = new_node ();
            }
            now = next[now][id];
        }
        end[now]++;
        f[now] = pos;   ///標記一下這個狀態對應第幾個字符串
    }
    void build () {   ///構建fail數組

        /**這裏備份一下next數組
        for(int i = 0; i < cnt; i++) {
            for(int j = 0; j < 26; j++) {
                next2[i][j] = next[i][j];
            }
        }

        這裏備份一下next數組**/


        queue <int> q;
        fail[root] = root;
        for (int i = 0; i < 26; i++) {
            if (next[root][i] == -1) {
                next[root][i] = root;
            }
            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 < 26; i++) {
                if (next[now][i] == -1) {
                    next[now][i] = next[fail[now]][i];
                }
                else {
                    fail[next[now][i]] = next[fail[now]][i];
                    q.push (next[now][i]);
                }
            }
        }

    }
    int query (char *buf) {


        int len = strlen (buf);
        int now = root;
        int res = 0;
        for(int i = 0; i < len; i++) {
            int id = buf[i]-'a';
            now = next[now][id];
            int tmp = now;
            while (tmp != root) {
                if (end[tmp]) {
                    res += end[tmp];
                    end[tmp] = 0; ///問題求的是多少把鑰匙能夠匹配,
                                  ///一種鑰匙匹配後就要把這種狀態給消掉,省的後面繼續算。
                }
                tmp = fail[tmp];//沿着失配邊走
            }
        }
        printf("%ld\n", res);

    }
}ac;
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        ac.init();
        for(int i = 1; i <= n; i++) {
            scanf("%s", str[i]);
            ac.insert(str[i], i);
        }
        ac.build();
        scanf("%s", a);
        ac.query(a);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章