●小集训之旅 一

 

有多大的思想,才有多大的能量。

    • 2017.3.27
      1. 学习内容:Aho-Corasick automaton(AC自动机)(原来不是自动AC机…)
      2. 算法用途:多模板串的模式匹配问题。
      3. 算法步骤:
        1. 用模板串构造Trie树(字典树 or 前缀树);
        2. 用bfs在Trie树中构建失配指针fail;
        3. 模式匹配(文本串和模板串进行匹配)
      4. HDU2222 Keywords Search(本题即为模版…)
        1. 一个裸题(入门练手用)(但要注意细节,不然要像我一样调两三节课,555)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#define N 500010 
using namespace std;
int trie[N][27],f[N],last[N],val[N],n,cnt,ans;
char T[2*N],P[55];
void make(int x)                            //求答案,与刘汝佳的print()相似 
{
	if(!x)return; 
	if(val[x]) ans+=val[x],val[x]=0;             
	make(last[x]);
}
void add_trie(char *P)                      //模板串加入Trie树 
{
    int x=0,i=0;char ch;
    while(P[i])
	{
        ch=P[i]-'a';
        if(!trie[x][ch])trie[x][ch]=++cnt;       
        x=trie[x][ch]; i++;
    }
    val[x]++;                               //有重复模板串 
}
void getfail()                              //bfs求fail[](and last[]) 
{
    queue<int> q; int rt,u,v;
    for(int i=0;i<26;i++) 
    if(trie[0][i]) q.push(trie[0][i]),f[trie[0][i]]=last[trie[0][i]]=0;
    while(!q.empty())
    {
        rt=q.front(); q.pop();
        for(int i=0;i<26;i++)
        {
            u=trie[rt][i]; 
            if(!u)continue; q.push(u);v=f[rt];
            while(v&&!trie[v][i])v=f[v];
            f[u]=trie[v][i];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
void Aho_Corasick_automaton(char *T)              //匹配 
{
    int x=0,ch;
    for(int i=0;T[i];i++)
    {
        ch=T[i]-'a';
        while(x&&!trie[x][ch])x=f[x];
        x=trie[x][ch];
        if(val[x]) make(x);
        else if(last[x]) make(last[x]);
    }
}
int main()
{
    int cas;scanf("%d",&cas);while(cas--) 
    {	
        memset(trie,0,sizeof(trie));              
        memset(val,0,sizeof(val));
        scanf("%d",&n); ans=0;cnt=0;  
        for(int i=1;i<=n;i++) scanf("%s",P), add_trie(P);
        getfail();                                     
        scanf("%s",T);
        Aho_Corasick_automaton(T);
        printf("%d\n",ans);
    }
    return 0;
}
/*	对于多组数据 : 
	trie[],val[] 必须memset
  	f[],last[] 不必memset,
  	因为(在bfs求fail值时)每个点的f和last值来源于之前的点
 	所以只需在往队列里加初值时,把对应的点的f和last赋值为0就行了 */
        1. 如汝佳所言(《训练指南》P216):“把所有不存在的边补上,即把计算失配函数中的语句 if(!u)continue; 改为 if(!u){trie[rt][i]=trie[f[rt]][i];continue;};这样while()便可直接删去。”  
        2. (个人认为原理同求last是一致的,与bfs的先进先出的特点有关) 改后的代码如下: 
void getfail()                              
{
    queue<int> q; int rt,u,v;
    for(int i=0;i<26;i++) 
    if(trie[0][i]) q.push(trie[0][i]),f[trie[0][i]]=last[trie[0][i]]=0;
    while(!q.empty())
    {
        rt=q.front(); q.pop();
        for(int i=0;i<26;i++)
        {
            u=trie[rt][i]; 
            if(!u){trie[rt][i]=trie[f[rt]][i];continue;};    //**
			q.push(u);v=f[rt];
            f[u]=trie[v][i];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
void Aho_Corasick_automaton(char *T)              
{
    int x=0,ch;
    for(int i=0;T[i];i++)
    {
        ch=T[i]-'a';
        x=trie[x][ch];
        if(val[x]) make(x);                                 //**
        else if(last[x]) make(last[x]);
    }
}
posted @ 2017-03-28 09:50  *ZJ  阅读(144)  评论(3编辑  收藏  举报