HDU2222-tire,AC自动机

来源:互联网 发布:龙腾世纪3 捏脸数据 编辑:程序博客网 时间:2024/05/16 16:59

好吧!为了以后复习,也为了与大家分享!我把书上的简介打到这里.

基本概念:

AC自动机是一种特殊的自动机,是通过在tires树上添加一些额外的边组成,其核心部分是后缀节点的构建。

算法:

AC自动机的构建分两步,首先通过所有的模式串建立起tire树,然后从树根向下一次计算每个节点的后缀节点。

AC自动机后缀的建立过程和kmp算法中next数组的求解过程十分相似,这里我们也用next数组来表示节点i对应的后节点的标号,fa[i]表示节点i在tire树上的父节点,此外假设

从fa[i]到i的那条边上的字符是c。其核心思想是首先看next[fa[i]]是否有条字符同样为c边,如果是,则next[i]即为next[fa[i]]标着c的那条边所指向的子节点。否则继续

检测next[next[fa[i]]]节点。如果通过不停的next还是无法找到满足条件的节点,则表示tire上没有串与节点i的路径字符串某个字符串后缀相同,那么其后缀

节点就为根,

     寻找next的伪代码:

    将root压人列队

    next[root]<- (-1);

    while(列队非空){

           x<-弹出队首。

           for(x标记为c的儿子i){
               tmp<-next[x]

               while(tmp!=-1且tmp不含有标记为c的儿子) tmp<-next[tmp];

               if(tmp=-1) next[i]<-root;

               else next[i]<-tmp的标记为c的边指向的节点。

               将i压入队列

               }

}

AC自动机的遍历和tire数很相似,对于主串s其遍历大概如下:

(1) 从根节点开始,从前向后一次查看s的每个字符c。

(2)如果当前节点x含有标记为c的边,沿着边走到下个节点。

(3) 否则,查看x的后缀节点next[x]。

如果next[x]含有标记为c的边,那么就沿着这条边走到next[x]的标记为c的那条边所指向的节点。

如果next[x]也没含有标记为c的边,继续查看next[x]的后缀节点next[next[x]],如果直到查看到根节点还无法找到满足条件的节点。那么就移动根节点。

(4) 如果还有字符,回到(2)。

为了理解看这张图。


   写了这么多就差不多了.

HDU2222这是到AC自动机人门题。代码如下:

#include<cstdio>#include<cstring>#define Max 500001using namespace std;char st[Max*2];int head,tail;struct node{node *next[26];node *fail;int cnt;node(){cnt=0;fail=NULL;memset(next,NULL,sizeof(next));}}*Q[Max];inline void insert(node *root){node *p=root;int i=0,idx;while(st[i]){idx=st[i]-'a';if(p->next[idx]==NULL) p->next[idx]=new node;p=p->next[idx];i++;}p->cnt++;}void built(node *root){root->fail=NULL;Q[head++]=root;while(head!=tail){node* tmp=Q[tail++];node* p=NULL;for(int i=0;i<26;i++){if(tmp->next[i]!=NULL){if(tmp==root) tmp->next[i]->fail=root;else{p=tmp->fail;while(p!=NULL){if(p->next[i]!=NULL){tmp->next[i]->fail=p->next[i];break;}p=p->fail;}if(p==NULL) tmp->next[i]->fail=root;}Q[head++]=tmp->next[i];}}}}int query(node *root){int i=0,res=0,idx;node *p=root;while(st[i]){idx=st[i]-'a';while(p->next[idx]==NULL&&p!=root) p=p->fail;p=p->next[idx];p=(p==NULL)?root:p;node *tmp=p;while(tmp!=root&&tmp->cnt!=-1){res+=tmp->cnt;tmp->cnt=-1;tmp=tmp->fail;}i++;}return res;}int main(){int n,t;scanf("%d",&t);while(t--){head=tail=0;node *root=new node;scanf("%d",&n);while(n--){scanf("%s",st);insert(root);}built(root);scanf("%s",st);printf("%d\n",query(root));}return 0;}


原创粉丝点击