hdu2222 ac自动机模板

题意:给出一系列字符串作为关键字,再给出一个字符串作为查询串,问查询串里包含多少关键字。

字符串匹配有kmp,保存大量字符串有字典树,所以当出现大量字符串需要匹配的情况,就有了ac自动机,这名字总让人想到一种很美好的歧义。。

就是数据插入字典树,构造自动机和把字符串放进去匹配三个基本操作把。



构造自动机规则如下:

如果某个节点的父节点就是根节点,那么这个节点的失败节点也就是根节点。

如果C的子节点中,有与A相同的节点B,则A的失败节点就是B

如果C的子节点中,没有与A相同的节点,且C就是根节点,则A的失败节点就是C

如果C的子节点中,没有与A相同的节点,但C不是根节点,则令C为C的失败节点,重复

查询规则如下:

在查找时,当前处在某个节点,往下无路可走时,那么就可以沿着fail指针跳到失败节点。

如果失败节点也无路可走,则跳到失败节点的失败节点,直至有路可走或者走到根节点为止。


思路就那么回事,真要写就纠结了。。

模板代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int maxn = 500010;
char str[1000010];

struct ACAutomaton{
    int next[maxn][26],fail[maxn],flag[maxn],last[maxn];
    int num,root;
    int newnode()
    {
        memset(next[num],0,sizeof(next[num]));
        flag[num] = 0;
        return num++;//每次都加,故num为结点数
    }

    void init()
    {
        num = 0;
        root = newnode();
    }

    void insert(char *str)
    {
        int len = strlen(str);
        int now = root;
        for(int i = 0;i < len;i++)
        {
            int &tmp = next[now][str[i]-'a'];//&取地址
            if(!tmp)
                tmp = newnode();//是空的那新建结点
            now = tmp;
        }
        flag[now]++;
    }

    void getfail()
    {
        queue<int> q;
        fail[root] = root;
        for(int i = 0;i < 26;i++)
        {
            int u = next[root][i];
            if(u)//头
            {
                fail[u] = last[u] = 0;
                q.push(u);
            }
        }
        while(!q.empty())
        {
            int now = q.front();q.pop();
            for(int i = 0;i < 26;i++)
            {
                int u = next[now][i];
                if(!u)
                    //这个点的第i个儿子指向now的失败节点的第i个儿子
                    next[now][i] = next[fail[now]][i];
                else
                {
                    fail[u] = next[fail[now]][i];//u的失败节点指向fail[now]的第i个儿子
                    last[u] = flag[fail[u]] ? fail[u]:last[fail[u]];//后缀链接
                    q.push(u);
                }
            }
        }
    }

    int query(char *str){
        int len = strlen(str);
        int now = root;
        int ret = 0;
        for(int i = 0;i < len;i++)
        {
            now = next[now][str[i]-'a'];
            int tmp = now;
            while(tmp != root && flag[tmp])
            {
                ret += flag[tmp];
                flag[tmp] = 0;//防止重复统计
                tmp = last[tmp];
            }
        }
        return ret;
    }
}ac;

int main(){
    int k,n;
    scanf("%d",&k);
    while(k--)
    {
        ac.init();
        scanf("%d",&n);
        for(int i = 0;i < n;i++)
        {
            scanf("%s",str);
            ac.insert(str);
        }
        ac.getfail();//类似kmp里的next
        scanf("%s",str);
        printf("%d\n",ac.query(str));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值