AC自动机模板题 P2017

Description

给出由若干单词组成的字典(可能有相同的单词),再给出一篇文章(一个字符串),请计算有多少个不同的单词在字符串中出现。

Input

第1行一个正整数N。表示字典中单词的数目。接下来的N行,每行一个字符串(长度不超过50),表示一个单词。最后一行为一个长字符串(长度不超过100000)。注意:单词和字符串均由小写字母构成。

Output

一行一个整数,表示长字符串中包含单词的数量。

Hint

N<=40000

Solution

AC自动机其实就是kmp+trie树(但实际上我觉得并没有kmp代码基本上还是按照trie树来的。。。),然后大概就是先把每个模式串按照前缀存进trie树,然后,,,,setnext数组,我是把next放进结构体里面存的所以还行,然后最后dofind的过程就是用主串来匹配模式串构成的trie树,然后就完了,,,挺好理解的就是代码写着令人智熄。。

注意事项: 1、newp要初始化成1因为是从1结点开始存的,0结点在setnext的过程中要用。不初始化会爆。。。 2、然后为了防止比如 abc ab 这种情况的时候如果就这样读的话读abc这个串的时候会读一个b标记的end_line和c标记的end_line,就已经把ab这个前缀读了,再读ab的话会重复,所以在cnt+=tree[k].end_line之后就把end_line给赋值为false就会防止读重复的情况了。 3、我是傻逼。。。数组要开到两百万我又只开了四万结果RE了一半。。有毒啊拉低我正确率我嚓本来我能一遍AC的。。。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define maxm 27
#define maxn 2000005
using namespace std;
struct Trie{
    int ch[maxm];
    bool end_line;
    int next;
}tree[maxn];
int newp=1,n,cnt;
char tmp[maxn],S[maxn];
void insert(char *s){
    int x=1,lenofS=strlen(s+1);
    for(int i=1;i<=lenofS;i++){
        int y=s[i]-'a';
        if(tree[x].ch[y]==0){
            tree[x].ch[y]=++newp;
        }
        x=tree[x].ch[y];
    }
    tree[x].end_line=true;
}
void setnext(){
    for(int i=0;i<26;i++){
        tree[0].ch[i]=1;
    }
    queue<int>q;
    q.push(1);
    tree[1].next=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++){
            if(tree[u].ch[i]==0){
                tree[u].ch[i]=tree[tree[u].next].ch[i];
            }
            else{
                q.push(tree[u].ch[i]);
                int v=tree[u].next;
                tree[tree[u].ch[i]].next=tree[v].ch[i];
            }
        }
    }
}
void dofindkmp(){
    int u=1,lenofS=strlen(S+1);
    for(int i=1;i<=lenofS;i++){
        int c=S[i]-'a';
        int k=tree[u].ch[c];
        while(k>1){
            cnt+=tree[k].end_line;
            tree[k].end_line=false;
            k=tree[k].next;
        }
        u=tree[u].ch[c];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",tmp+1);
        insert(tmp);
    }
    scanf("%s",S+1);
    setnext();
    dofindkmp();
    printf("%d\n",cnt);
    return 0;
}

转载于:https://www.cnblogs.com/virtual-north-Illya/p/10045139.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值