AC自动机其实就是Trie+KMP,AC自动机还可与稍微扩展一下,扩展成Trie图的形式(每次fial的时候可以一步到位,不需要一步一步慢慢跳)
很好的一篇博客trie树和优化成trie图都说的很清楚
#include
#include
using namespace std;
const int N = 1e4 + 10, S = 55, M = 1e6 + 10;
int n, T;
int cnt[N * S], q[N * S], tr[N * S][26], ne[N * S], idx;
char str[M];
void insert()
{
int p = 0;
for (int i = 0; str[i]; ++ i)
{
int t = str[i] - 'a';
if (!tr[p][t]) tr[p][t] = ++ idx;
p = tr[p][t];
}
cnt[p] ++;
}
void build()//建立trie树
{
int hh = 0, tt = -1;
for (int i = 0; i < 26; ++ i)
{
if (tr[0][i]) q[++tt] = tr[0][i];//之前没加if TLE了
}
while (hh <= tt)
{
int t = q[hh++];
for (int i = 0; i < 26; ++ i)
{
int p = tr[t][i];
if (p)
{
int j = ne[t];
while (j && !tr[j][i]) j = ne[j];
if (tr[j][i]) j = tr[j][i];
ne[p] = j;
q[++tt] = p;
}
}
}
}
void match()//匹配
{
int res = 0;
cin >> str;
for (int i = 0, j = 0; str[i]; ++ i)
{
int t = str[i] - 'a';
while (j && !tr[j][t]) j = ne[j];
if (tr[j][t]) j = tr[j][t];
int p = j;
while (p)
{
res += cnt[p];
cnt[p] = 0;
p = ne[p];
}
}
cout << res << endl;
}
void solve()
{
memset(cnt, 0, sizeof cnt);
memset(tr, 0, sizeof tr);
memset(ne, 0, sizeof ne);
idx = 0;
cin >> n;
for (int i = 0; i < n; ++ i)
{
cin >> str;
insert();
}
build();
match();
}
int main()
{
cin >> T;
while (T --) solve();
return 0;
}
优化成trie图
#include
#include
using namespace std;
const int N = 1e4 + 10, S = 55, M = 1e6 + 10;
int n, T;
int cnt[N * S], q[N * S], tr[N * S][26], ne[N * S], idx;
char str[M];
void insert()
{
int p = 0;
for (int i = 0; str[i]; ++ i)
{
int t = str[i] - 'a';
if (!tr[p][t]) tr[p][t] = ++ idx;
p = tr[p][t];
}
cnt[p] ++;
}
void build()//建立trie树
{
int hh = 0, tt = -1;
for (int i = 0; i < 26; ++ i)
{
if (tr[0][i]) q[++tt] = tr[0][i];//之前没加if TLE了
}
while (hh <= tt)
{
int t = q[hh++];
for (int i = 0; i < 26; ++ i)
{
int p = tr[t][i];
//优化 相当于并查集路径压缩的思想
if(!p) tr[t][i] = tr[ne[t]][i];//如果不存在t的子节点i
else
{
ne[p] = tr[ne[t]][i];
q[++tt] = p;
}
}
}
}
void match()//匹配
{
int res = 0;
cin >> str;
for (int i = 0, j = 0; str[i]; ++ i)
{
int t = str[i] - 'a';
j = tr[j][t];//不用一步一步跳判断是否存在tr[j][t]了,最差的情况是直接指向根节点
int p = j;
while (p)
{
res += cnt[p];
cnt[p] = 0;
p = ne[p];
}
}
cout << res << endl;
}
void solve()
{
memset(cnt, 0, sizeof cnt);
memset(tr, 0, sizeof tr);
memset(ne, 0, sizeof ne);
idx = 0;
cin >> n;
for (int i = 0; i < n; ++ i)
{
cin >> str;
insert();
}
build();
match();
}
int main()
{
cin >> T;
while (T --) solve();
return 0;
}
优化到真正的线性复杂度
