AC自动机

  AC自动机是著名的多模匹配算法之一。常见的例子就是给出n个单词,再给你包含m个字符的文章,问你有多少个单词在文章中出现过。

  其实AC自动机是以字典树和KMP的基础上实现的。

  首先要构造一个Tire,然后再在上面构造失配然后再匹配。

  失配(fail)指针:使当前字符失配时跳转到具有最长公共前后缀的字符继续匹配。

  但是我现在还是不怎么懂失配是怎么弄的,先留个坑,后面来填。

  (8.14补)今天听了实验室的人讲的AC自动机,感觉理解也深了。

  其实就是从最长的那个串按着失配指针一只往前跳,我们知道失配指针是指向当前字符串的最长后缀的,那么我们不会漏下任何一个。

  所以跟着失配指针我们可以找到所有能匹配的个数(因为建树会在节点标记他是否是字符串结尾)。

  模板:

/*  gyt
       Live up to every day            */

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstring>`
#include<queue>
#include<set>
#include<string>
#include<map>
#include <time.h>
#define PI acos(-1)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 10000+5;
const ll maxm = 1e7;
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f;
const ll inf = 1e15 + 5;
const db eps = 1e-9;
const int kind=26;
struct node{
    node *fail;
    node *next[kind];
    int coun;
    void nodee() {
        fail=NULL;
        coun=0;
        for (int i=0; i<kind; i++)
            next[i]=NULL;
    }
}*root;
char str[1000000+100];

void updata() {
    node *p=root;
    int len=strlen(str);
    for (int i=0; i<len; i++) {
        int pos=str[i]-'a';
        if (p->next[pos]==NULL) {
            p->next[pos]=new node;
            p->next[pos]->nodee();
            p=p->next[pos];
        }
        else  p=p->next[pos];
    }
    p->coun++;
   // cout<<p->coun<<endl;
}
void getfail() {
    node *p=root, *son, *tmp;
    queue<struct node*>que;
    que.push(p);
    while(!que.empty()) {
        tmp=que.front();
        que.pop();
        for (int i=0; i<26; i++) {
            son=tmp->next[i];
            if (son!=NULL) {
                if (tmp==root) {
                    son->fail=root;
                }
                else {
                    p=tmp->fail;
                    while(p) {
                        if (p->next[i]) {
                            son->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if (!p)  son->fail=root;
                }
                que.push(son);
            }
        }
    }
}
void query() {
    int len=strlen(str);
    node *p, *tmp;
    p=root;
    int cnt=0;
    for (int i=0; i<len; i++) {
        int pos=str[i]-'a';
        while(!p->next[pos]&& p!=root)  p=p->fail;
        p=p->next[pos];
        if (!p)  p=root;
        tmp=p;
        while(tmp!=root) {
            if (tmp->coun>=0) {
                cnt+=tmp->coun;
                tmp->coun=-1;
            }
            else  break;
            tmp=tmp->fail;
        }
        //cout<<cnt<<endl;
    }
    printf("%d
", cnt);
}
void solve() {
    root=new node;
    root->nodee();
    root->fail=NULL;
    int n;   scanf("%d", &n);
    getchar();
    for (int i=0; i<n; i++) {
        gets(str);  updata();
    }
    getfail();
    gets(str);
    query();
}
int main() {
    int t = 1;
   // freopen("in.txt", "r", stdin);
    scanf("%d", &t);
    while(t--)
        solve();
    return 0;
}

   如果要求在字符串里出现的次数,我们再在结构体里定义一个id,表示编号,query里面在变变

void query(char *str) {
    int len=strlen(str);
    node *p, *tmp;
    p=root;
    int cntt=0;
    for (int i=0; i<len; i++) {
        int pos=str[i]-'A';
        while(!p->next[pos]&& p!=root)  p=p->fail;
        p=p->next[pos];
        if (!p)  p=root;
        tmp=p;
        while(tmp!=root && tmp->id>0) {
            //cout<<1<<endl;
            int aa=tmp->id;
          //  cout<<aa<<endl;
            cnt[aa]++;
            tmp=tmp->fail;
        }
    }
}
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstring>`
#include<queue>
#include<set>
#include<string>
#include<map>
#include <time.h>
#define PI acos(-1)
#include<cstdio>
#include<cstring>
using namespace std;

const int kind = 26; //有多少种字符
const int N = 1005;
const int M = 2000005;
struct node
{
    node *next[kind];
    node *fail;
    int id;//病毒编号
    node() {
        for(int i = 0; i < kind; i++)
            next[i] = NULL;
        fail = NULL;
        id = 0;
    }
}*q[51*N];
node *root;
int head, tail;
char source[M], s[M];
char vir[N][55];
int cnt[N];

void updata(char *str, int id) {
    node *p=root;
    int len=strlen(str);
    for (int i=0; i<len; i++) {
        int pos=str[i]-'A';
        if (p->next[pos]==NULL) {
            p->next[pos]=new node();
            //p->next[pos]->nodee();
            p=p->next[pos];
        }
        else  p=p->next[pos];
    }
 //   p->coun++;
    p->id = id;
   // cout<<p->coun<<endl;
}
void getfail() {
    node *p=root, *son, *tmp;
    queue<struct node*>que;
    que.push(p);
    while(!que.empty()) {
        tmp=que.front();
        que.pop();
        for (int i=0; i<26; i++) {
            son=tmp->next[i];
            if (son!=NULL) {
                if (tmp==root) {
                    son->fail=root;
                }
                else {
                    p=tmp->fail;
                    while(p) {
                        if (p->next[i]) {
                            son->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if (!p)  son->fail=root;
                }
                que.push(son);
            }
        }
    }
}
void query(char *str) {
    int len=strlen(str);
    node *p, *tmp;
    p=root;
    int cntt=0;
    for (int i=0; i<len; i++) {
        int pos=str[i]-'A';
        while(!p->next[pos]&& p!=root)  p=p->fail;
        p=p->next[pos];
        if (!p)  p=root;
        tmp=p;
        while(tmp!=root && tmp->id>0) {
            //cout<<1<<endl;
            int aa=tmp->id;
          //  cout<<aa<<endl;
            cnt[aa]++;
            tmp=tmp->fail;
        }
    }
}
int main()
{
    int n;
   // freopen("in.txt", "r", stdin);
    while(~scanf("%d",&n)) {
        memset(cnt, 0, sizeof(cnt));
        head = tail = 0;
        root = new node();
      for(int i = 1; i <= n; i++) {
            scanf("%s", vir[i]);
            updata(vir[i], i);
        }
        getfail();
        scanf("%s",source);
        int len = strlen(source);
        int l = 0;
        for(int i = 0; i <= len; i++) {
            if(source[i] >= 'A' && source[i] <= 'Z')
                s[l++] = source[i];
            else {
                s[l] = '';
                query(s);
                l = 0;
            }
        }
        for(int i = 1; i <= n; i++)
            if(cnt[i])
                printf("%s: %d
",vir[i], cnt[i]);
    }
    return 0;
}

   数组版AC自动机(hdu 6208)

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=1e5+100;
const int maxm=50*10010;
const int SIGMA_SIZE=26;
int n;
char t[maxn],s[maxn];

struct AC  {
    int ch[maxm][26];
    int val[maxm];
    int fail[maxm],last[maxm];
    int sz;
    void clear(){memset(ch[0],0,sizeof(ch[0]));sz=1;}
    int idx(char x){return x-'a';}
    void insert(char *s) {
        int u=0;
        int n=strlen(s);
        for(int i=0;i<n;i++) {
            int c=idx(s[i]);
            if(!ch[u][c])
            {
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz]=0;
                ch[u][c]=sz++;
            }
            u=ch[u][c];
        }
        val[u]++;
    }
    void getfail() {
        queue<int> q;
        fail[0]=0;
        int u=0;
        for(int i=0;i<SIGMA_SIZE;i++) {
            u=ch[0][i];
            if(u){q.push(u);fail[u]=0;last[u]=0;}
        }
        while(!q.empty()) {
            int r=q.front();q.pop();
            for(int i=0;i<SIGMA_SIZE;i++) {
                u=ch[r][i];
                if(!u){
                        ch[r][i]=ch[fail[r]][i];
                    continue;
                }
                q.push(u);
                int v=fail[r];
                while(v&&!ch[v][i])v=fail[v];
                fail[u]=ch[v][i];
                last[u]=val[fail[u]]?fail[u]:last[fail[u]];
            }
        }
    }
    int find(char *s)  {
        int u=0,cnt=0;
        int n=strlen(s);
        for(int i=0;i<n;i++)  {
            int c=idx(s[i]);
            u=ch[u][c];
            int temp=0;//必须赋初值为0,表示如果下面两个判断都不成立的时候while可以正常执行
            if(val[u])
                temp=u;
            else if(last[u])
                temp=last[u];
            while(temp)  {
                cnt+=val[temp];
                val[temp]=0;
                temp=last[temp];
            }
        }
        return cnt;
    }
}tree;
string a[maxn];
void solve()  {
    scanf("%d",&n);
    tree.clear();
    int maxx=0, id=0;
    for(int i=1;i<=n;i++) {
        scanf("%s",t);
        tree.insert(t);
        int len=strlen(t);
        if (len>maxx) {
            maxx=len;
            id=i;
        }
        a[i]=string(t);
    }
    tree.getfail();
    int len=a[id].length();
    for (int i=0; i<len; i++)
        t[i]=a[id][i];
    int ans=tree.find(t);
    if (ans==n)  puts(t);
    else  puts("No");
}
int main() {
    int t = 1;
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    scanf("%d", &t);
    while(t--)
        solve();
    return 0;
}
原文地址:https://www.cnblogs.com/gggyt/p/7327099.html