zoukankan      html  css  js  c++  java
  • 小结:ac自动机

    复杂度:

    查找O(n),维护O(n)

    概要:

    应用了kmp的自匹配思想,在trie建图时维护一个fali指针,指向上一个匹配的点,这点是用bfs做到。匹配串的时候同样没匹配到就和kmp一样返回。

    应用:单串匹配多模板,维护多模板里边的信息。

    技巧及注意:

    插入和trie一样,然后是bfs。

    在bfs的过程中,注意以根开头的不需要匹配fail,即ch[0][i]不需要匹配他们的fail,即为空。

    然后在查找的时候当找到一个时我们要遍历这个节点的整个fail树,然后取出信息后记得将信息清空。

    一定要注意!节点数是不超过n个串的总长度!!!一定要切记!!!

    注意!!bfs的时候一定要维护信息!!!!

    例题:

    1. 【BZOJ】1030: [JSOI2007]文本生成器(递推+ac自动机)(将自动机的节点作为转移的状态然后递推)
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << #x << " = " << x << endl
    #define printarr(a, n, m) rep(aaa, n) { rep(bbb, m) cout << a[aaa][bbb]; cout << endl; }
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    inline const int max(const int &a, const int &b) { return a>b?a:b; }
    inline const int min(const int &a, const int &b) { return a<b?a:b; }
    
    using namespace std;
    
    const int N=10000000, Type=26;
    int T[N][Type], w[N], fail[N], cnt, q[N], front, tail, ans;
    char str[1000000], st[1000000];
    
    inline void ins(char s[]) {
    	int len=strlen(s), now=0;
    	rep(i, len) {
    		int t=s[i]-'a';
    		if(!T[now][t]) T[now][t]=++cnt;
    		now=T[now][t];
    	}
    	++w[now];
    }
    void bfs() {
    	int now=0;
    	q[tail++]=0;
    	while(tail!=front) {
    		now=q[front++]; if(front==N) front=0;
    		rep(i, 26) if(T[now][i]) {
    			q[tail++]=T[now][i]; if(tail==N) tail=0;
    			if(now==0) continue;
    			int p=fail[now];
    			while(p && !T[p][i]) p=fail[p];
    			fail[T[now][i]]=T[p][i];
    		}
    	}
    }
    void ac(char s[]) {
    	int len=strlen(s), now=0;
    	rep(i, len) {
    		int t=s[i]-'a';
    		while(now && !T[now][t]) now=fail[now];
    		now=T[now][t];
    		t=now;
    		while(t) {
    			ans+=w[t];
    			w[t]=0;
    			t=fail[t];
    		}
    	}
    }
    int main() {
    	scanf("%s", str);
    	int n=getint();
    	while(n--) {
    		scanf("%s", st);
    		ins(st);
    	}
    	bfs();
    	ac(str);
    	print(ans);
    	return 0;
    }
    

    没错,就是AC自动机!(可惜不像金坷垃那样~)

    不多说,放上模板,不难理解的。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define for1(i,a,n) for(i=a;i<=n;++i)
    #define for2(i,a,n) for(i=a;i<n;++i)
    #define for3(i,a,n) for(i=n;i>=a;--i)
    #define for4(i,a,n) for(i=n;i>a;--i)
    #define CC(i,a) memset(i, a, sizeof(i))
    
    using namespace std;
    
    const int N=10000000, Type=26;
    char a[N], b[N];
    int ch[N][Type], fail[N], w[N], ans, cnt=1;
    queue<int> q;
    
    void ins(char* s) {
    	int u=0, v, i, len=strlen(s);
    	for2(i, 0, len) {
    		v=s[i]-'a';
    		if(!ch[u][v]) ch[u][v]=cnt++;
    		u=ch[u][v];
    	}
    	w[u]++;
    }
    
    void bfs() {
    	fail[0]=0;
    	q.push(0);
    	int u, p, i;
    	while(!q.empty()) {
    		u=q.front(); q.pop();
    		for2(i, 0, Type) if(ch[u][i]) {
    			q.push(ch[u][i]); //先插入,后判断
    			if(!u) continue; //不要在这里错了。。
    			p=fail[u];
    			while(p && !ch[p][i]) p=fail[p];
    			fail[ch[u][i]]=ch[p][i];
    		}
    	}
    }
    
    void ac(char* s) {
    	int i, u=0, v, len=strlen(s), t;
    	for2(i, 0, len) {
    		v=s[i]-'a';
    		while(u && !ch[u][v]) u=fail[u];
    		u=ch[u][v];
    		t=u;
    		while(t) {
    			ans+=w[t];
    			w[t]=0;
    			t=fail[t];
    		}
    	}
    
    }
    
    void init() {
    	CC(fail, 0);
    	CC(ch, 0);
    	CC(w, 0);
    }
    
    int main() {
    	init();
    	scanf("%s", a);
    	int n, i;
    	scanf("%d", &n);
    	for1(i, 1, n) {
    		scanf("%s", b);
    		ins(b);
    	}
    	bfs();
    	ac(a);
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    

     注意一些细节即可

  • 相关阅读:
    Linux ansible的group模块
    ansible copy 模块详解
    Linux centos yum仓库 自制
    ansible 的playbook脚本
    Linux centos 监控备份
    Linux centos nginx下载安装初步
    周总结5
    周总结4
    爬取
    结对开发
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/3859358.html
Copyright © 2011-2022 走看看