zoukankan      html  css  js  c++  java
  • AC自动机

    AC自动机

    其实就是 Trie + kmp......

    AC自动机常适用于多模式串匹配中,效率和KMP处理单模式串匹配相当

    解决的问题诸如:

    shehesayshrher
    以上哪些模式串在文本串 yasherhs 中出现过

    概述

    首先对这些模式串建一棵trie树:

    然后对每个节点求一个next数组

    这里对next数组重新定义一下:对于节点 (i) , (next[i]) 表示从根节点到 (i) 的路径代表的字符串的后缀能够在trie树中匹配到的最长路径的指针。

    文字叙述绕得离谱,画画图。

    图也很乱...... 结合下面表格凑合着理解吧...

    对next数组我们可以逐层求解,用上一层求解下一层(BFS)

    这里实现的时候手写了队列

    void build()//构建AC自动机
    {
    	int head=0,tail=-1;
    	for(int i=0;i<26;i++)
    	{
    		if(tr[0][i])
    			q[++tail]=tr[0][i];//初始化
    	}
    
    	while(head<=tail)
    	{
    		int x=q[head++];//当前节点
    		for(int i=0;i<26;i++)
    		{
    			int y=tr[x][i];//子节点
    			if(!y) continue;//判断此节点是否存在
    			int j=nxt[x];//用已经算好的上一个next继续算
    			while(j && !tr[j][i]) j=nxt[j];//匹配失败就跳走
    			if(tr[j][i]) j=tr[j][i];
    			nxt[y]=j;
    			q[++tail]=y;//入队,继续扩展
    		}
    	}
    }
    
    

    与kmp相同的一点是,查询与构造时的操作都是差不多的,这里直接放全代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=10010,S=55,M=1000010;
    
    int n;
    int tr[N*S][26],cnt[N*S];
    char str[M];
    int q[N*S],nxt[N*S],idx;
    
    void insert(char* str)//trie插入字符串
    {
    	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()//构建AC自动机
    {
    	int head=0,tail=-1;
    	for(int i=0;i<26;i++)
    	{
    		if(tr[0][i])
    			q[++tail]=tr[0][i];//初始化
    	}
    
    	while(head<=tail)
    	{
    		int x=q[head++];//当前节点
    		for(int i=0;i<26;i++)
    		{
    			int y=tr[x][i];//子节点
    			if(!y) continue;//判断此节点是否存在
    			int j=nxt[x];//用已经算好的上一个next继续算
    			while(j && !tr[j][i]) j=nxt[j];//匹配失败就跳走
    			if(tr[j][i]) j=tr[j][i];
    			nxt[y]=j;
    			q[++tail]=y;//入队,继续扩展
    		}
    	}
    }
    
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while(T--)
    	{
    		memset(tr,0,sizeof tr);
    		memset(cnt,0,sizeof cnt);
    		memset(nxt,0,sizeof nxt);
    		idx=0;
    
    		scanf("%d",&n);
    		for(int i=0;i<n;i++)
    		{
    			scanf("%s",str);
    			insert(str);
    		}
    		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=nxt[j];
    			if(tr[j][t]) j=tr[j][t];
    
    			int p=j;
    			while(p)
    			{
    				res+=cnt[p];
    				cnt[p]=0;
    				p=nxt[p];
    			}
    		}
    
    		printf("%d
    ",res);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    C++11常用特性的使用经验总结
    Websocket协议的学习、调研和实现
    高性能后台服务器架构设计
    Linux下ping命令、traceroute命令、tracert命令的使用
    docker-4-Dockerfile配置文件详解
    Docker 国内仓库和镜像
    centos7.0查看IP
    tomcat报错:This is very likely to create a memory leak问题解决
    windows 环境下搭建docker私有仓库
    Docker 修改镜像源地址
  • 原文地址:https://www.cnblogs.com/IzayoiMiku/p/14347910.html
Copyright © 2011-2022 走看看