http://acm.hdu.edu.cn/showproblem.php?pid=2222
AC自动机模板题
数组版本(参考kuangbin和昀神)写的精简巧妙,很喜欢.
(找失败指针中,原来node[i][j]表示字典树i节点的字符为j的儿子的编号。
现在扩展一下,如果i没有字符j这个儿子那么node[i][j]就是i节点沿着失败指针一直走到有字符为j的儿子的节点的编号,若不存在,指向根。相当于一个路径压缩的思想)
Code:
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn =1000010;
struct Trie
{
int node[maxn][26],fail[maxn],num[maxn],root,L;
int newnode()
{
for(int i=0;i<26;i++) node[L][i]=-1;
num[L]=0;
return L++;
}
void init(){L=0,root=newnode();}
void insert_fun(char buf[])
{
int now=root;
int len=strlen(buf);
for(int i=0;i<len;i++)
{
if(node[now][buf[i]-'a']==-1)
node[now][buf[i]-'a']=newnode();
now=node[now][buf[i]-'a'];
}
num[now]++;
}
void build()
{
queue<int> q;
fail[root]=root;
for(int i=0;i<26;i++)
if(node[root][i]==-1)
node[root][i]=root;
else
{
fail[node[root][i]]=root;
q.push(node[root][i]);
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(node[now][i]==-1)
node[now][i]=node[fail[now]][i]; //将该边指向当前节点fail指针指向的相应字符连接的节点
else
{ //将儿子节点的fail指针指向当前节点fail指针指向相应字符接的节点
fail[node[now][i]]=node[fail[now]][i];
q.push(node[now][i]);
}
}
}
}
int query(char buf[])
{
int len=strlen(buf);
int now=root,res=0;
for(int i=0;i<len;i++)
{
now=node[now][buf[i]-'a'];
int temp=now;
while(temp!=root&& num[temp]!=-1)
{
res+=num[temp];
num[temp]=-1;
temp=fail[temp];
}
}
return res;
}
};
Trie ac;
char buf[maxn];
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ac.init();
for(int i=0;i<n;i++)
{
scanf("%s",buf);
ac.insert_fun(buf);
}
ac.build();
scanf("%s",buf);
int ans=ac.query(buf);
printf("%d\n",ans);
}
return 0;
}
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define T_SIZE 1000000
#define P_SIZE 50
#define TOTAL_P 10000
struct trie{ //利用结构体来封装字典树的节点
trie* next[26];
trie* fail;
int num;
trie()
{
for(int i=0;i<26;i++)
next[i]=NULL;
fail=NULL;
num=0;
}
};
char T[T_SIZE+1];
char P[P_SIZE+1];
trie *q[TOTAL_P*P_SIZE];
void insert(trie* root,char* s)
{
trie *p=root;
for(int i=0;s[i]!='\0';i++)
{
if(p->next[s[i]-'a']==NULL)
p->next[s[i]-'a']=new trie;
p=p->next[s[i]-'a'];
}
p->num++;
}
void build_ac_automation(trie* root)
//利用广搜构建失败指针
{
int head=0,tail=0;
q[tail++]=root;
while(head!=tail)
{
trie *front=q[head++];
//front为队头
for(int i=0;i<26;i++)
if(front->next[i]!=NULL)
//遍历队头元素的子节点
{
trie* p=front->fail;
while(p!=NULL)
//只有根节点的失败指针为NULL
{
if(p->next[i]!=NULL)
{
front->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL)
front->next[i]->fail=root;
q[tail++]=front->next[i];
}
}
}
int ac_find(trie* root,char* T)
{
trie* p =root;
int sum=0;
for(int i=0,len=strlen(T);i<len;i++)
{
while(p->next[T[i]-'a']==NULL&& p!=root)
{
p=p->fail;
}
if(p->next[T[i]-'a']!=NULL)
p=p->next[T[i]-'a'];
trie* temp =p;
while(temp!=root && temp->num!=-1)
{
sum+=temp->num;
temp->num=-1;
temp=temp->fail;
}
}
return sum;
}
int main()
{
int t;
for(scanf("%d",&t);t>0;t--)
{
trie *root =new trie;
int n;
scanf("%d",&n);
getchar();
for(int i=0;i<n;i++)
{
gets(P);
insert(root,P);
}
build_ac_automation(root);
gets(T);
printf("%d\n",ac_find(root,T));
}
return 0;
}
魔改版
#include <bits/stdc++.h>
using namespace std;
#define forn(i, n) for(int i = 0; i < (int)(n); ++i)
#define fail ((now !=root) ? nd[fa[now]][i] : root)
#define id(a) (a - 'a')
const int maxn =1000010;
struct acAuto {
int nd[maxn][26], fa[maxn], num[maxn], root, L;
int newnode() {forn(i, 26) nd[L][i] = -1; return num[L] = 0, L++;}
void init() {L = 0, root = newnode();}
void insert(string s) {
int now = root;
for(auto i : s) now = nd[now][id(i)] = (~nd[now][id(i)] ? nd[now][id(i)] : newnode());
num[now]++;
}
void build() {
queue<int> q;
q.push(root);
while(!q.empty()) {
int now = q.front(); q.pop();
forn(i, 26) ~nd[now][i] ? (q.push(nd[now][i]), fa[nd[now][i]] = fail) : nd[now][i] = fail;
}
}
int query(string s) {
int now = root, res = 0, t;
for(auto i : s) {
now = nd[now][id(i)], t = now;
while(t != root && ~num[t]) res += num[t], num[t] = -1, t = fa[t];
}
return res;
}
}ac;
int T, n;
string s;
int main() {
ios::sync_with_stdio(false);cin.tie(0);
cin >> T;
while(T--) {
ac.init();
cin >> n;
forn(i, n) ac.insert((cin >> s , s));
cout << ac.query((ac.build(), cin >> s, s)) << endl;
}
return 0;
}