【HDOJ2222】Keywords Search(AC自动机)

problem

  • 给定n(< 1e4)个长度不超过50的单词(模式串)和一篇长为m(< 1e6)的文章(主串)。
  • 求多少个单词在文章中出现了。

solution

  • AC自动机模板
    1、将所有模式串构建成字典树
    2、求出nxt数组,其中nxt[i]表示匹配到i节点i的转移边都不能匹配了,满足最长前后缀关系的新节点。
    3、继续匹配

codes

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 5e5+5;

int tot, tire[maxn][30], val[maxn], nxt[maxn];
void build(){
    tot = 1;
    memset(val,0,sizeof(val));
    memset(tire,0,sizeof(tire));
}
void insert(char *s){
    int u = 1, len = strlen(s);
    for(int i = 0; i < len; i++){
        int c = s[i]-'a';
        if(tire[u][c]==0)tire[u][c] = ++tot;
        u = tire[u][c];
    }
    val[u]++;
    return ;
}
int find(char *s){
    int ans = 0;
    int u = 1, len = strlen(s);
    for(int i = 0; i < len; i++){
        int c = s[i]-'a';
        int k = tire[u][c];
        while(k > 1){
            ans += val[k];
            val[k] = 0;
            k = nxt[k];
        }
        u = tire[u][c];
    }
    return ans;
}
void genfail(){
    for(int i = 0; i < 26; i++)tire[0][i] = 1;
    queue<int>q;
    q.push(1); nxt[1] = 0;
    while(q.size()){
        int u = q.front();  q.pop();
        for(int i = 0; i < 26; i++){
            if(!tire[u][i])tire[u][i] = tire[nxt[u]][i];//如果不存在u->i转移边,就直接回到第一个满足字符i的节点
            else{
                q.push(tire[u][i]);
                nxt[tire[u][i]] = tire[nxt[u]][i];
            }
        }
    }
}

char s[maxn<<1];
int main(){
    int T;  scanf("%d",&T);
    while(T--){
        build();
        int n;  scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%s",s);
            insert(s);
        }
        genfail();
        scanf("%s",s);
        printf("%d\n",find(s));
    }
    return 0;
}
posted @ 2018-08-11 10:12  gwj1139177410  阅读(115)  评论(0编辑  收藏  举报
选择