您的位置:首页 > 其它

hdu 2222 Keywords Search AC自动机模板题

2017-02-24 17:07 288 查看
点击打开链接

题意:给出母串S 长度小于1e6 ,和字典, 问s中出现的dic单词有多少个?

KMP是当字典中单词个数为1的情况 若单词个数很多 则复杂都O(n*m)显然不行 于是AC自动机就来啦~

参考

AC自动机=KMP+Trie
AC自动机主要就3个部分:    
 1 把所有模板插入到Trie中,建立大的状态转移图
 2 建立失败指针:

    根结点的失败指针指向自己
    每个结点的失败指针是从父节点的失败指针指向的结点的子结点中寻找与这个结点相同的结点

     和kmp一样:后缀失配时找到合适的前缀继续匹配

同时 顺手把不存在的边都给补上,顺手建last数组 

last作用:若到i匹配成功,将母串中以后缀i结尾的单词进行快速匹配

当母串中以后缀i的和某个模式串S匹配成功,则该模式串沿着失配边,找到前缀为模式串T,T和后缀s相同,同样也和母串后缀i相同 

3 模式匹配:因为失配函数的时候顺手把不存在的边都给补上了,直接顺着失配边走下去就好了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+20;
struct Aho{
int chd
[26],v
,f
,last
,sz,ans;
void init()
{
sz=1;ans=0;
memset(v,0,sizeof(v));
memset(f,0,sizeof(f));
memset(chd[0],0,sizeof(chd[0]));
}
void insert(char *s)
{
int u=0,i=0;
while(s[i])
{
int id=s[i]-'a';
if(!chd[u][id])
{
memset(chd[sz],0,sizeof(chd[sz]));
chd[u][id]=sz++;
}
u=chd[u][id];
i++;
}
v[u]++;
}
void getFail()
{
queue<int> q;
f[0]=0;
for(int c=0;c<26;c++)
{
int u=chd[0][c];
if(u)
{
f[u]=0; q.push(u);last[u]=0;
}
}
while(!q.empty())
{
int r=q.front();q.pop();
for(int c=0;c<26;c++)
{
int u=chd[r][c];
if(!u)
{
chd[r][c]=chd[f[r]][c];
continue;
//补边
}
q.push(u);
int x=f[r];
//找到前缀i和后缀c相等
while(x&&!chd[x][c])
x=f[x];
f[u]=chd[x][c];
//last作用:若到i匹配成功,将母串中以后缀i结尾的单词进行快速匹配
//当母串中以后缀i的和某个模式串S匹配成功,则该模式串沿着失配边,找到前缀为模式串T,T和后缀s相同,同样也和母串后缀i相同
last[u]=v[f[u]]? f[u]:last[f[u]];
}
}
}
void solve(int j)
{
if(!j) return;
if(v[j])
{
ans+=v[j];
v[j]=0;
}
solve(last[j]);
}
void find(char *s)
{
int n=strlen(s),j=0;
getFail();
for(int i=0;i<n;i++)
{
int id=s[i]-'a';
j=chd[j][id];//trie
if(v[j])
solve(j);//匹配成功 看还有没有其他串和后缀i匹配
else if(last[j])
solve(last[j]);//
}
}
}aho;
char s
;
int main()
{
int t;
cin>>t;
while(t--)
{
aho.init();
char dic[100];
int n;
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%s",dic);
aho.insert(dic);
}
scanf("%s",s);
aho.find(s);
cout<<aho.ans<<endl;
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: