字符串——AC自动机模板(收集)

模板来源于博客:https://bestsort.cn/2019/04/28/402/

文本串中模板串的总数

int tot=0;//编号
int trie[N][26];//字典树
int val[N];//字符串结尾标记
int fail[N];//失配指针
void insert(char * s){//插入模式串
    int root=0;//字典树上当前匹配到的结点
    for(int i=0;s[i];i++){
        int id=s[i]-'a';//子节点编号
        if(trie[root][id]==0)//若之前没有从root到id的前缀
            trie[root][id]=++tot;//插入
        root=trie[root][id];//顺着字典树往下走
    }
    val[root]++;
}
void build(){//构建fail指针域建立字典图
    memset(fail,0,sizeof(fail));
    queue<int>q;
    for(int i=0;i<26;i++)//将根节点的子节点入队
        if(trie[0][i])
            q.push(trie[0][i]);

    while(!q.empty()){
        int k=q.front();//对于队首节点k,其fail指针已求得,现在要求的是他子节点的fail指针
        q.pop();
        for(int i=0;i<26;i++){//遍历字符集
            if(trie[k][i]){//若字符i对应的子节点存在
                fail[trie[k][i]]=trie[fail[k]][i];//将这个子节点fail指针赋给fail[k]的字符i对应的节点
                q.push(trie[k][i]);
            }
            else
                trie[k][i]=trie[fail[k]][i];//将fail[k]的子节点直接赋成k的子节点
        }
    }
}
int query(char *t){//对文本串进行匹配
    int res=0;//存储结果
    int root=0;//字典树上当前匹配到的结点
    for(int i=0;t[i];i++){//对文本串进行遍历
        int id=t[i]-'a';//子节点编号
        root=trie[root][id];//在字典图中不断穿梭跳动
        int j=root;
        while(j&&val[j]!=-1){//利用fail指针找出所有匹配的模式串
            res+=val[j];//累加到答案中
            val[j]=-1;
            j=fail[j];//fail指针跳转
        }
    }
    return res;
}
char P[N];
char T[N];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        memset(trie,0,sizeof(trie));
        memset(val,0,sizeof(val));
        memset(fail,0,sizeof(fail));
        tot=0;

        int n;//模式串个数
        scanf("%d",&n);
        while(n--){
            scanf("%s",P);//输入模式串
            insert(P);//插入字典树中
        }
        build();//构建失配指针与字典图

        scanf("%s",T);//输入文本串
        int res=query(T);
        printf("%d\n",res);
    }
    return 0;
}

文本串中单个模板串的个数

int res[N];
struct AC_Automata{
    int tire[N][26];//字典树
    int val[N];//字符串结尾标记
    int fail[N];//失配指针
    int last[N];//last[i]=j表j节点表示的单词是i节点单词的后缀,且j节点是单词节点
    int tot;//编号

    void init(){//初始化0号点
        tot=1;
        val[0]=0;
        last[0]=0;
        fail[0]=0;
        memset(tire[0],0,sizeof(tire[0]));
    }

    void insert(char *s,int v){//构造trie与val数组,v需非0,表示一个单词节点
        int len=strlen(s);
        int root=0;
        for(int i=0;i<len;i++){
            int id=s[i]-'a';
            if(tire[root][id]==0){
                tire[root][id]=tot;
                memset(tire[tot],0,sizeof(tire[tot]));
                val[tot++]=0;
            }
            root=tire[root][id];
        }
        val[root]=v;
    }

    void build(){//构造fail与last
        queue<int> q;
        for(int i=0;i<26;i++){
            int root=tire[0][i];
            if(root!=0){
                fail[root]=0;
                last[root]=0;
                q.push(root);
            }
        }

        while(!q.empty()){//bfs求fail
            int k=q.front();
            q.pop();
            for(int i=0;i<26; i++){
                int u=tire[k][i];
                if(u==0)
                    continue;
                q.push(u);

                int v=fail[k];
                while(v && tire[v][i]==0)
                    v=fail[v];
                fail[u]=tire[v][i];
                last[u]=val[fail[u]]?fail[u]:last[fail[u]];
            }
        }
    }

    void print(int i){//递归打印与结点i后缀相同的前缀节点编号
        if(val[i]){
            res[val[i]]++;
            print(last[i]);
        }
    }

    void query(char *s){//匹配
        int len=strlen(s);
        int j=0;
        for(int i=0;i<len;i++){
            int id=s[i]-'a';
            while(j && tire[j][id]==0)
                j=fail[j];
            j=tire[j][id];
            if(val[j])
                print(j);
            else if(last[j])
                print(last[j]);
        }
    }
}ac;
char P[1000][1000];
char T[N];
int main(){
    int n;
    while(scanf("%d",&n)!=EOF&&n){
        memset(res,0,sizeof(res));
        ac.init();

        for(int i=1;i<=n;i++){
            scanf("%s",P[i]);
            ac.insert(P[i],i);
        }
        ac.build();

        scanf("%s",T);
        ac.query(T);
        for(int i=1;i<=n;i++)
            if(res[i])
                printf("%s: %d\n",P[i],res[i]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值