zoukankan      html  css  js  c++  java
  • P3796 【模板】AC自动机(加强版) 题解(Aho-Corasick Automation)

    题目链接

    AC自动机

    解题思路

    AC自动机模板题。
    刚学AC自动机,写一篇博客增强理解。
    AC自动机最关键的一点在于,(fail)失配指针的构造。
    (fail)指针指向的地方,是匹配出现错误后进行重新匹配的位置,这说明,从根开始到(fail)指针指向的地方这一块字符串,正是我们刚刚失配之前配上的那一块字符串(子串),且为最长子串。这一点和KMP算法相同。

    AC代码

    #include<stdio.h>
    #include<string.h>
    int ac[100010][26],cnt=1;
    int queue[100010],fail[100010],end[100010];
    char a[160][75],m[1000010];
    struct {
    	int num,cnt;
    }ans[500],temp;
    void push(char a[],int l,int num){//建立trie树,很好理解不再赘述
    	int i,now=0;
    	for(i=0;i<l;i++){
    		int v=a[i]-'a';
    		if(!ac[now][v])ac[now][v]=cnt++;
    		now=ac[now][v];
    	}
    	end[now]=num;
    }
    void build(){
    	int head=0,tail=0,i;//C党手写queue
    	for(i=0;i<26;i++)if(ac[0][i]){
    		queue[tail++]=ac[0][i];//push
    		fail[ac[0][i]]=0;
    	}
    	while(head<tail){
    		int v=queue[head++];//pop
    		for(i=0;i<26;i++){
    			if(ac[v][i]){
    				fail[ac[v][i]]=ac[fail[v]][i];
    				queue[tail++]=ac[v][i];//push
    			}
    			else ac[v][i]=ac[fail[v]][i];
    			//该节点的失配指针,指向该节点的父节点的失配指针所指向的节点的子节点
    			//也即,假设ac[v][i]的父节点失配指针指向的节点为E,则
    			//从根节点到E的这个串,为从根节点到v这个串的最长共同结尾子串
    			//那么,ac[v][i]的失配指针应当指向E的第i个节点(保证这一位字符相同)
    			//if和else的作用:一个是失配指针,一个是trie图。
    			//如果这点没有后续了,只能建立trie图。
    			//否则,应当建立失配指针。 
    		}
    	}
    }
    void query(char a[],int l){
    	int i,j,now=0;
    	for(i=0;i<l;i++){
    		now=ac[now][a[i]-'a'];//沿着建立好的trie图走
    		for(j=now;j;j=fail[j])ans[end[j]].cnt++;//找到单词末尾并存储个数
    	}
    }
    //C党手写快排
    int cmp(int x,int y){
    	if(ans[x].cnt>ans[y].cnt)return 1;
    	if(ans[x].cnt<ans[y].cnt)return 0;
    	if(ans[x].num<ans[y].num)return 1;
    	return 0;
    }
    void qs(int left,int right){
    	int i=left,j=right;
    	if(i>=j)return;
    	while(i!=j){
    		for(;i<j;j--)if(cmp(j,left))break;
    		for(;i<j;i++)if(cmp(left,i))break;
    		if(i!=j){
    			temp=ans[i];ans[i]=ans[j];ans[j]=temp;
    		}
    	}
    	j=left;
    	temp=ans[i];ans[i]=ans[j];ans[j]=temp;
    	qs(left,i-1);
    	qs(i+1,right);
    }
    int main(){
    	int i,n;
    	while(scanf("%d",&n)){
    		if(!n)break;
    		cnt=1;
    		for(i=1;i<=n;i++){
    			scanf("%s",a[i]);
    			push(a[i],strlen(a[i]),i);//建立trie树
    			ans[i].num=i;
    			ans[i].cnt=0;
    		}
    		build();//构造AC自动机的fail指针,以及完善trie树成为trie图
    		scanf("%s",m);
    		query(m,strlen(m));//询问文本串
    		//以下为本题特色,不是AC自动机精髓,可跳过
    		qs(1,n);
    		printf("%d
    ",ans[1].cnt);
    		printf("%s
    ",a[ans[1].num]);
    		for(i=2;i<n;i++){
    			if(ans[i].cnt-ans[i-1].cnt)break;
    			printf("%s
    ",a[ans[i].num]);
    		}
    		memset(end,0,sizeof(int)*cnt);
    		memset(fail,0,sizeof(int)*cnt);	
    		memset(ac,0,sizeof(ac));	
    	}
    	return 0;
    }
    
  • 相关阅读:
    Codeforces 723d [暴力dfs]
    Codeforces 723e [图论][欧拉回路]
    Hihocoder 1035 [树形dp]
    Codeforces 721C [dp][拓扑排序]
    Codeforces 721D [贪心]
    info
    关于string操作
    Floyd求最小环 HDU1599
    Codeforces Round #572 (Div. 2) B Number Circle
    A. XXXXX
  • 原文地址:https://www.cnblogs.com/Potassium/p/10079569.html
Copyright © 2011-2022 走看看