AC自动机(模板)

给定 n 个长度不超过 5050 的由小写英文字母组成的单词,以及一篇长为 m的文章。

请问,其中有多少个单词在文章中出现了。

注意:每个单词不论在文章中出现多少次,仅累计 1 次。

输入格式

第一行包含整数 T,表示共有 T组测试数据。

对于每组数据,第一行一个整数 n,接下去 n 行表示 n 个单词,最后一行输入一个字符串,表示文章。

输出格式

对于每组数据,输出一个占一行的整数,表示有多少个单词在文章中出现。

数据范围

1≤n≤104,
1≤m≤106

输入样例:

1
5
she
he
say
shr
her
yasherhs

输出样例:

3

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
constexpr int N=10010,S=55,M=10000010;
int n,tr[N*S][26],cnt[N*S],idx;
char str[M];
int q[N*S],ne[N*S];

void insert(){
    int p=0;
    for(int i=0;str[i];i++){
        int t=str[i]-'a';
        if(!tr[p][t])tr[p][t]=++idx;
        p=tr[p][t];
    }
    cnt[p]++;
}
void build(){
    int hh=0,tt=-1;
    for(int i=0;i<26;i++){
        if(tr[0][i])
            q[++tt]=tr[0][i];
    }
    while(hh<=tt){
        int t=q[hh++];
        for(int i=0;i<26;i++){
            int c=tr[t][i];
            if(!c) continue;
            int j=ne[t];
            while(j&&!tr[j][i]) j=ne[j];
            if(tr[j][i]) j=tr[j][i];
            ne[c]=j;
            q[++tt]=c;
        }
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(tr,0,sizeof tr);
        memset(cnt,0,sizeof cnt);
        memset(ne,0,sizeof ne);
        idx=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",str);
            insert();
        }
        build();
        scanf("%s",str);
        int res=0;
        for(int i=0,j=0;str[i];i++){
            int t=str[i]-'a';
            while(j&&!tr[j][t]) j=ne[j];
            if(tr[j][t]) j=tr[j][t];
            int p=j;
            while(p){
                res+=cnt[p];
                cnt[p]=0;
                p=ne[p];
            }
        }
        printf("%d\n",res);
    }
    return 0;
}

上面的容易被卡 ,下面的防卡

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
constexpr int N=1000010,M=1e6+10;
int n,tr[N][26],cnt[N],idx;
char str[M];
int ne[N];

void insert(){
    int p=0;
    for(int i=0;str[i];i++){
        int t=str[i]-'a';
        if(!tr[p][t])tr[p][t]=++idx;
        p=tr[p][t];
    }
    cnt[p]++;
}
void build(){
    queue<int> q;
    for(int i = 0 ; i < 26 ; ++i){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int t = q.front();q.pop();
        for(int i = 0 ; i < 26 ; ++i){
            int &p = tr[t][i];
            if(!p) p = tr[ne[t]][i];
            else{
                ne[p] = tr[ne[t]][i];
                q.push(p);
            }
        }
    }
}
int main(){
        idx=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",str);
            insert();
        }
        build();
        scanf("%s",str);
        int res=0;
        for(int i=0,j=0;str[i];i++){
            int t = str[i] - 'a';
            j = tr[j][t];
            int p = j;
            while(p and cnt[p] != -1){
                res += cnt[p];
                cnt[p] = -1;
                p = ne[p];
            }
        }
        printf("%d\n",res);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

q619718

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值