自动AC机

自动AC机利用Trie树的特性,结合KMP算法的失配边思想,通过构建失败链接进行模式匹配。在实现时,如果当前节点的某个子节点不存在,则将其连接到失败指针对应的子节点,简化了循环过程,提高了效率。模板题hdu2222中,需要找出大字符串中模式串出现的次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自动AC机就是trie上的kmp,由于trie从跟到任意节点都是模式串中存在的一个子串,所以可以类比KMP,从每个节点向另一个节点连一条失配边(KMP的失配边就是next数组,因此可视为trie为一条链)。实现的过程中有一点优化,就是如果一个点的某个儿子不存在,把他连向fail指针的该儿子。这样就不用写循环了,清爽了很多。

模板题:hdu2222

题意:给定一个大字符串和若干模式串,求有多少模式串在大字符串中出现过。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000005;
const int MAXS = 10005 * 50;

int T, N;
char s1[55], s[MAXN];
int que[MAXS];
struct Trie
{
    int ch[MAXS][26], cnt[MAXS], fail[MAXS];
    bool vis[MAXS];
    int ncnt;
    void init()
    {
        for (int i = 0; i<26; ++i)
            ch[0][i] = 1;
        for (int i = 1; i<=ncnt; ++i) {
            for (int j = 0; j<26; ++j) ch[i][j] = 0;
            vis[i] = 0, fail[i] = cnt[i] = 0;
        }
        ncnt = 1;
    }
    void ins(char *s)
    {
        int p = 1;
        for (int i = 0, c; s[i]; ++i)
        {
            c = s[i] - 'a';
            if (!ch[p][c]) p = ch[p][c] = ++ncnt;
            else p = ch[p][c];
        }
        ++cnt[p];
    }
    void build()
    {
        int l = 0, r = 0, t, p, i;
        fail[que[l] = 1] = 0;
        while (l <= r) {
            t = que[l ++];
            for (i = 0; i < 26; ++i) {
                if (!ch[t][i]) continue;
                p = fail[t];
                while (!ch[p][i]) p = fail[p];
                fail[ch[t][i]] = ch[p][i];
                que[++r] = ch[t][i];
            }
        }
    }
    int quary(char *s)
    {
        int k = 1, c, ans = 0;
        for (int i = 0; s[i]; ++i) {
            vis[k] = 1;
            c = s[i] - 'a';
            while (!ch[k][c]) k = fail[k];
            k = ch[k][c];
            if (!vis[k])
                for (int j = k; j; j = fail[j])
                    ans += cnt[j], cnt[j] = 0;
        }
        return ans;
    }
} ac;

int main()
{
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &N);
        ac.init();
        while (N --)
            scanf("%s", s1), ac.ins(s1);
        ac.build();
        scanf("%s", s);
        printf("%d\n", ac.quary(s));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值