文章标题 HDU 2222 : Keywords Search (AC自动机模板)

题目链接
题意:有n个单词,然后有一个文本串,问有多少个单词出现在文本串中出现,每个单词只技术一次
分析:ac自动机模板题,得注意单词只计数一次。
代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=500000+10;
int n;
int ch[maxn][26];//trie中每个点的儿子节点编号
int fail[maxn];
int last[maxn];//后缀链接:沿着i的失配指针走遇到的下一个单词节点的编号
int val[maxn];//点上的标记(比如标记是否是单词节点)
int sz;
int root;
int newnode (){//新节点 
    memset (ch[sz],0,sizeof (ch[sz]));//将其孩子置为0 
    val[sz]=0;
    return sz++;
}
void init(){//初始化 
    sz=0;
    root=newnode();
}
int insert(char *str){//建树 
    int len=strlen(str);
    int now=root;
    for (int i=0;i<len;i++){
        int c=str[i]-'a'; 
        if (!ch[now][c]){
            memset (ch[sz],0,sizeof (ch[sz]));
            val[sz] = 0;
            ch[now][c] = sz++;
        }
        now=ch[now][c];
    }
    val[now]++;
    return now;
}
void getfail(){
    queue<int>q;
    fail[root]=root;
    for (int i=0;i<26;i++){
        int u=ch[root][i];
        if (u){
            fail[u]=0;
            last[u]=0;
            q.push(u);
        }
    }
    while (!q.empty()){
        int now=q.front();q.pop();
        for (int i=0;i<26;i++){
            int u=ch[now][i];
            //u=ch[now][i]就是我们说的当前节点A,now是A的父节点,
            //那么fail[now]就相当于我们说的父节点的失败节点C
            if (!u){
                /*
                如果当前节点now没有i这个儿子u,
                直接让这个点的第i个儿子u指向now的失败节点的第i个儿子。
                因为如果一个串在AC自动机里面走,走到now时要走的下一个节点是i,
                now又没有i这个儿子,因此一定会是失配的。
                既然失配了,now就要沿着fail指针走,试图去匹配失败节点的第i个儿子。
                这里的ch[now][i]=ch[fail[now]][i]就相当于
                让他直接去匹配now的失败节点的第i个儿子
                */
                ch[now][i]=ch[fail[now]][i];
            }
            else {
                //如果当前节点now有i这个儿子u,
                //那么u的失败节点显然要指向C(即fail[now])的第i个儿子
                fail[u]=ch[fail[now]][i];
                last[u]=val[fail[u]]?fail[u]:last[fail[u]];
                q.push(u);
            }
        }
    }
}
int quary(char *str){
    int len=strlen(str);
    int now=root;
    int ans=0;
    for (int i=0;i<len;i++){
        now=ch[now][str[i]-'a'];
        int tmp=now;
        if (tmp!=root&&!val[tmp]&&last[tmp]){
            tmp=last[tmp];
        }
        while (tmp!=root&&val[tmp]){
            ans+=val[tmp];
            val[tmp]=0;
            tmp=last[tmp];
        }
    }
    return ans;
}
char text[1000005];
char str[55];
int main()
{
    int t;
    scanf("%d",&t);
    while (t--){ 
        init();
        scanf ("%d",&n);
        for (int i=0;i<n;i++){
            scanf ("%s",str);
            insert (str);
        }
        getfail();
        scanf ("%s",text);
        printf ("%d\n",quary(text));
    } 
    return 0;
 } 
/*
100
2
c
abcd
abcde
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值