题目链接:https://vjudge.net/problem/HDU-5880
转自:https://blog.csdn.net/qq_41625562/article/details/82938348
题意:有n个模板串,有一个文本串,将文本串中的所有模板串换成‘*’。
思路:通过这个题加深了对AC自动机的理解,把递归查找换成了while循环查找。last数组白书上解释的是可以直接找到串结尾而不必每次通过fail数组判断是否是一个串的结尾。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include<map>
#include<string>
using namespace std;
const int SIGMA_SIZE = 26;
const int MAXNODE = 1000005;
const int MAXS = 150 + 10;
struct ACautomata
{
int ch[MAXNODE][SIGMA_SIZE];
int f[MAXNODE]; // fail函数
int val[MAXNODE]; // 每个字符串的结尾结点都有一个非0的val
int last[MAXNODE]; // 输出链表的下一个结点
int sz;
void init()
{
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
memset(val, 0, sizeof(val));
}
// 字符c的编号
int idx(char c)
{
return c - 'a';
}
// 插入字符串。v必须非0
void insert(char *s, int v)
{
int u = 0, n = strlen(s);
for (int i = 0; i < n; 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] = v;
}
// 在T中找模板
void find(char* s)
{
int n = strlen(s);
int p = 0; // 当前结点编号,初始为根结点
for (int i = 0; i < n; i++)
{
if(s[i]>='A'&&s[i]<='Z'||s[i]>='a'&&s[i]<='z')
{
if(s[i]>='A'&&s[i]<='Z')
p=ch[p][s[i]-'A'];
else
p=ch[p][s[i]-'a'];
int tmp=p;//一定要注意不能直接用p查找
while(tmp!=0) //while循环查找
{
for(int j=0; j<val[tmp]; j++) //从模式串的末尾,依次改到模式串的首位
s[i-j]='*';
tmp=last[tmp];
}
}
}
}
// 计算fail函数
void getFail()
{
queue<int> q;
f[0] = 0;
// 初始化队列
for (int c = 0; c < SIGMA_SIZE; c++)
{
int u = ch[0][c];
if (u)
{
f[u] = 0;
q.push(u);
last[u] = 0;
}
}
// 按BFS顺序计算fail
while (!q.empty())
{
int r = q.front();
q.pop();
for (int c = 0; c < SIGMA_SIZE; 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]];
}
}
}
};
char text[1000010],p[1000005];
ACautomata ac;
int main()
{
int n,T;
scanf("%d",&T);
while (T--)
{
ac.init();
scanf("%d",&n);
for (int i = 1; i <=n; i++)
{
scanf("%s",p);
ac.insert(p, strlen(p));
}
getchar();
ac.getFail();
gets(text);
ac.find(text);
printf("%s\n",text);
}
return 0;
}