题目链接
学习AC自动机的第一道题(可能跟广大学友是一样的),让我知道了什么是AC自动机。
具体讲一下吧,它就是用来求多串匹配的(而KMP只是求单串匹配的,相当于是在KMP上做了优化)。之后,就是怎么构造AC自动机了,知道它就是在一棵字典树上做文章,我们先对所要查询的字符串作字典树的建立,并且,在每个字符串的尾,也就是每个字符串的尾部在字典树上的值附上"+1",若是能查到这一步,就说明是可以取的一个(或多个完整的单词),然后,我们就可以去拿到它了。
与KMP一样,它也是需要关系传递的,那么怎么做到传递关系?就是要写一个KMP中的next[]数组一样作用的指针了,这里就叫做fail指针吧,其意义就如同它的名字一样,若是下一步匹配失败,我们可以跳转到哪个相同的点继续判断是一个道理的,但是,这个指针只能跳转到一个更浅的地方去,并且还要保证相同的后缀。
求fail指针的方法倒也不难,我们可以用一个BFS的思想再带上KMP思想可以合并得到,这样就能有AC自动机的fail指针了。我们利用BFS是为了深度的传递,不能传递到更高深度去,只能向浅的地方去,然后,我们对出队的节点temp,考虑它的后面的26个next[]情况,如果它是有next[]的,我们就对它的下一个节点去找有没有匹配的,就利用再来个新的指针p去查询,这个指针是出队节点的fail指向,于是,若是p->next[i]不为空的时候,就是第一个找到的时候了,那么直接给temp->next[i]->fail = p->next[i];即可,不然,就是直接还到root节点。接下来,再对temp->next[i]入队,我们继续去查询就是了。
最后,就是索要答案的时候了,我们对模式串,去查询它有多少个单词的覆盖,首先,跟KMP一样,若是目前查到的节点不是虚构的root,就是说前面有匹配的时候,我们得让它的下一个节点也匹配上,那么就是若p->next[x] == NULL;我们就可以去p = p->fail;然后,以此类推,直到找到符合条件的要求的点,或者是空的点(也就是虚假存在的root)。以此,我们不断的去查询,查询完的点赋为"-1"(表示取过了),然后在此之前,取它的值,然后再去取所有它的fail的值,因为既然该数可以,它的fail都只是它的后缀,说明也是可以的,直接取了就是了。最后,就能得到的匹配的数目。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e6 + 7;
int N, ans;
char sss[55], las[maxN];
struct node
{
node *next[26];
node *fail;
int sum;
};
node *root, *new_node;
void update(char *s)
{
node *temp = root;
int len = (int)strlen(s);
for(int i=0; i<len; i++)
{
int x = s[i] - 'a';
if(temp->next[x] == NULL)
{
new_node = (node *)malloc(sizeof(node));
for(int j=0; j<26; j++) new_node->next[j] = NULL;
new_node->sum = 0; new_node->fail = NULL;
temp->next[x] = new_node;
}
temp = temp->next[x];
}
temp->sum++;
}
queue<node *> Q;
void build_fail()
{
Q.push(root);
node *p, *temp;
while(!Q.empty())
{
temp = Q.front(); Q.pop();
for(int i=0; i<26; i++)
{
if(temp->next[i])
{
if(temp == root) temp->next[i]->fail = root;
else
{
p = temp->fail;
while(p)
{
if(p->next[i])
{
temp->next[i]->fail = p->next[i];
break;
}
p = p->fail;
}
if(p == NULL) temp->next[i]->fail = root;
}
Q.push(temp->next[i]);
}
}
}
}
void AC_auto(char *s)
{
node *p = root;
int len = (int)strlen(s);
for(int i=0; i<len; i++)
{
int x = s[i] - 'a';
while(p != root && !p->next[x]) p = p->fail;
p = p->next[x];
if(!p) p = root;
node *temp = p;
while(temp != root && temp->sum >= 0)
{
ans += temp->sum;
temp->sum = -1;
temp = temp->fail;
}
}
}
inline void init()
{
root = (node * )malloc(sizeof(node));
for(int i=0; i<26; i++) root->next[i] = NULL;
root->fail = NULL;
root->sum = 0;
ans = 0;
while(!Q.empty()) Q.pop();
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
init();
scanf("%d", &N);
for(int i=1; i<=N; i++)
{
scanf("%s", sss);
update(sss);
}
scanf("%s", las);
build_fail();
AC_auto(las);
printf("%d\n", ans);
}
return 0;
}