搞了一个AC自动机的题目,看了白书上的讲解,其实就是Trie和KMP的结合,不过白书上的last数组不是很好理解的。搞这个知识点一个下午结果还是有点迷迷糊糊,结果大手子们告诉我这个可以套板子的。然后就拿了一个板子往里面套。
这个题目要注意的是可能输入的keywords会可能重复的,那样的话在插入insert的时候就需要对叶子结点进行++,而不是置为1或者某个值,然后在find的时候就可以进行累加了,每次读取文本的一个字母后进行查询下一个字母的标号然后沿着失配边走,累加叶子结点的val值即可。画图理解。
例子可以采用he she his hers为keywords 然后text为 shershis 能理解好在求fail失配指针同时加的一个补边操作ch[r][c]=ch[f[r]][c];
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
#include <queue>
using namespace std;
const int N = 500010;
char text[1000010], P[10010][80];
int n;
struct AhoCorasickAutomata
{
int ch[N][26], val[N], last[N], f[N];//,cnt[N];
int sz;
void init() { sz = 1; val[0]=0; memset(ch[0], 0, sizeof(ch[0])); }
int idx(char c) { return c - 'a'; }
void inser(char * s)
{
int u = 0, len = strlen(s);
for(int i = 0; i < len; i++)
{
int c = idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u]++;
}
void getfail()
{
queue<int> q;
f[0] = 0;
for(int c = 0; c < 26; c++)
{
int u = ch[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 = ch[r][c];
if(!u)
{
ch[r][c] = ch[f[r]][c];
continue;
}
q.push(u);
int v = f[r];
while(v && !ch[v][c]) v = f[v];
f[u] = ch[v][c];
last[u] = val[f[u]]? f[u]: last[f[u]];
}
}
}
int mfind(char * T)
{
int ss=0;
int len = strlen(T), j = 0;
for(int i = 0; i < len; i++)
{
j = ch[j][idx(T[i])];
int tmp=j;
while(tmp!=0)
{
ss+=val[tmp];
val[tmp]=0;
tmp=f[tmp];
}
/*if(val[j]) print(j);
else if(last[j]) print(last[j]);*/
}
return ss;
}
/*
void print(int j)
{
if(j)
{
cnt[val[j]]++;
print(last[j]);
}
}*/
}ac;
void initation()
{
ac.init();
for(int i = 1; i <= n; i++)
{
scanf("%s", P[i]);
ac.inser(P[i]);
}
scanf("%s", text);
}
void solve()
{
ac.getfail();
printf("%d\n",ac.mfind(text));
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d", &n);
initation();
solve();
}
return 0;
}