HDU2222
【题目大意】求给定n个模式串在主串中出现了几个。(每一个模式串只被记录一次)
【题解】暴力AC自动机。
今天在搞字符串专题,发现AC自动机这一块内容还是掌握得不熟练,觉得还是要巩固。不打不知道,打了发现还是会有很多要注意的地方。(无数次RE...)
要牢记fail指针的求法,与KMP算法类比。
用文字不太好表达,还是好好看代码吧。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for (int i = a;i <= b;i ++)
using namespace std;
const int maxn = 1000005;
const int maxm = 250010;
int T,N,tot,len;
char s[maxn];
struct Node
{
int son[26];
int fail,cnt;
}trie[maxm];
void Insert(int x,int i)
{
if (!trie[x].son[s[i]-'a'])
{
trie[x].son[s[i]-'a'] = ++ tot;
if (i != len)Insert(tot,i+1);
} else if (i != len) Insert(trie[x].son[s[i]-'a'],i+1);
if (i == len) trie[trie[x].son[s[i]-'a']].cnt ++;
}
void Clear()
{
fo(i,0,tot)
{
memset(trie[i].son,0,sizeof trie[i].son);
trie[i].fail = 0;
trie[i].cnt = 0;
}
tot = 0;
}
void Make_trie()
{
while (N --)
{
scanf("%s",s+1);
len = strlen(s+1);
Insert(0,1);
}
}
void Construct_fail()
{
static int d[maxm];
int l = 0, r = 1;
while (l < r)
{
int x = d[++l];
fo(i,0,25)
if (trie[x].son[i])
{
int v = trie[x].son[i];
if (x == 0) trie[v].fail = 0;
else
{
int p = trie[x].fail;
while (p && !trie[p].son[i]) p = trie[p].fail;
if (trie[p].son[i]) trie[v].fail = trie[p].son[i];
else trie[v].fail = 0;
}
d[++r] = v;
}
}
}
void Query()
{
int p = 0, ans = 0;
fo(i,1,len)
{
int x = s[i] - 'a';
while (p && !trie[p].son[x]) p = trie[p].fail;
if (trie[p].son[x]) p = trie[p].son[x];
else p = 0;
int temp = p;
while (temp && trie[temp].cnt != -1)
{
ans += trie[temp].cnt;
trie[temp].cnt = -1;
temp = trie[temp].fail;
}
}
printf("%d\n",ans);
}
int main()
{
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
scanf("%d",&T);
while (T --)
{
scanf("%d",&N);
Clear();
Make_trie();
Construct_fail();
scanf("%s",s+1);
len = strlen(s+1);
Query();
}
return 0;
}
这里每个模式串只统计一次,trie[temp].cnt == -1时说明temp以及之后的fail的节点都已被统计过。
while (temp && trie[temp].cnt != -1)
{
ans += trie[temp].cnt;
trie[temp].cnt = -1;
temp = trie[temp].fail;
}