AC自动机

来源:互联网 发布:锦尚中国源码 编辑:程序博客网 时间:2024/06/04 18:55

AC自动机用来解决多个模式串与文本穿的匹配问题,它结合和KMP和前缀输的思想,建立一颗tree图,匹配的时间复杂度为O(n)。

hduoj2222AC自动机模板题目;

#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<climits>#include<cctype>#include<iostream>#include<algorithm>#include<queue>#include<vector>#include<map>#include<string>#include<set>#include<stack>#define ll long long#define MAX 250000#define MAXN 100#define INF INT_MAX#define N 26#define eps 1e-8using namespace std;char s[1000010],t[MAXN];struct Node{Node* ch[N];          //ch[]指向子节点,当ch[]不为空时,则对应子节点存在 Node* pre;            //失配指针 int w;                //记录以该节点为结尾的模式串的个数,用来统计匹配数 bool isend;           //是否为模式串的结尾节点 }a[MAX];void init(){               //初始化节点 for (int i=0; i<MAX; i++){a[i].pre = NULL;a[i].isend = false;a[i].w = 0;memset(a[i].ch,0,sizeof(a[i].ch));}}int cnt;void insert(Node* root){           //在树中插入新的模式串,用于建tree树 ,和tree一样 for (int i=0; i < strlen(t); i++){int u = t[i] - 'a';if (root->ch[u] == NULL){root->ch[u] = a + cnt;cnt++;}root = root->ch[u];}root->isend = true;root->w += 1;}/*求前缀指针的步骤,按照深度与一一求出每个节点的失配指针。对于前节点root->ch[i],如果root->pre所指的节点的儿子中,有ch[i]对应的字符,那么当前节点的失配指针指向该儿子节点,否则通过父亲节点的失配指针所指的节点继续按失配指针查找*/ void getPre(){              //求每个节点的失配指针,类似于KMP中失配指针的求法,此处用bfs顺序递推 for (int i=0; i<N; i++) a[0].ch[i] = a + 1;   //区别a[1]和a + 1;a[0].pre = NULL;        //增加一个辅助节点a[0],它的所有子节点指向a[1],失配指针为NULL,同时使a[1]的失配指针指向它 a[1].pre = a;queue<Node*> q;q.push(a+1);while (!q.empty()){                    Node* root = q.front();q.pop();for(int i=0; i<N; i++){Node* p = root->ch[i];if (p != NULL){Node* pt = root->pre;while (pt){if (pt->ch[i]){p->pre = pt->ch[i];if (p->pre->isend){       //如过某节点通过失配指针所指向的节点是一个字符的结尾节点,那么该节点也看做一个字符的结尾节点,但并不用更新计算该节点的w p->isend = true;}break;}else pt = pt->pre;}q.push(p);} }}}int query(){int n = strlen(s);int res = 0;Node* p = a + 1;for (int i=0; i < n; i++){int u = s[i] - 'a';while(true){if (p->ch[u]){p = p->ch[u];if (p->isend){Node* pt = p;while (pt != NULL){    //同一个节点可能对应多个字符串的结尾。当找到一个模板串后,应该顺着失配指针往回走,看看有没有其它串 res += pt->w;pt->w = 0;        //pt->w计算过后要置0,防止重复计算 pt = pt->pre;}}break;}else p = p->pre;}}return res;}int main(){int T,n;scanf("%d",&T);while (T--){init();cnt = 2;                   //注意cnt应初始化为2,0和1已经被辅助节点和tree树根占用 scanf("%d",&n);for (int i=0; i<n; i++){scanf("%s",t);insert(a+1);             //思考为什么要以a+1为tree树的根 }getPre();scanf("%s",s);printf("%d\n",query());}return 0;}


0 0
原创粉丝点击