AC自动机
很多人形容ac自动机的思路为trie树上的KMP,不得不说确实很像,唯一的区别在于指针的意义。
fail指针指向一个字串的最长后缀。由它的定义可以得出其一个很简单、很常用的性质:如果trie上的一个节点在目标串中出现了,那么它的fail指针指向的节点也会出现(而且结尾位置和该串一致)。这个性质可以在多模式匹配时用于状态转移(这一点很像kmp)。
trie树中exit数组的思想在这里依然适用,统计答案时很方便。
【ac掉的题目】
P3808 AC自动机模板题,模板题,多模式串、单目标串匹配计数。这个题oi-wiki上有题解,写的听不错的,可以拿来当ac自动机的模板。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ldb;
#define pb push_back
int n;
const int maxn = 1e6 + 5;
char s[maxn], t[maxn];
namespace ac
{
const int N = 1e6 + 5;
int trie[N][26], e[N];
int tot = 0;
int fail[N];
void insert(char *r)
{
int p = 0;
for (int i = 0; r[i]; ++i)
{
int c = r[i] - 'a';
if (!trie[p][c])
trie[p][c] = ++tot;
p = trie[p][c];
}
e[p]++;
}
void build()
{ // build ac graph
queue<int> q;
for (int i = 0; i < 26; ++i)
if (trie[0][i])
q.push(trie[0][i]);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < 26; ++i)
{
if (trie[u][i])
{
fail[trie[u][i]] = trie[fail[u]][i];
q.push(trie[u][i]);
}
else
trie[u][i] = trie[fail[u]][i];
}
}
}
int query(char *r)
{
int u = 0, ans = 0;
for (int i = 0; r[i]; ++i)
{
u = trie[u][r[i] - 'a'];
for (int j = u; j && e[j] != -1; j = fail[j])
{
ans += e[j];
e[j] = -1;
}
}
return ans;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ain3", "r", stdin);
#endif
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%s", s);
ac::insert(s);
}
ac::build();
scanf("%s", t);
printf("%d\n", ac::query(t));
return 0;
}
P3796 AC自动机模板加强版 ,稍微不那么板(其实也挺板的),计数题,没有卡时间。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ldb;
#define pb push_back
const int N = 11000; // number of nodes
const int maxn = 1e6 + 5; // max length of target string
namespace ac
{
int tot;
int e[N], tr[N][26];
int f[N];
char s[155][75];
map<int, int> m;
int ans[155];
void init()
{
tot = 0;
memset(e, 0, sizeof(e));
memset(tr, 0, sizeof(tr));
memset(f, 0, sizeof(f));
m.clear();
memset(ans, 0, sizeof(ans));
}
void insert(int id)
{
scanf("%s", s[id]);
int u = 0;
for (int i = 0; s[id][i]; ++i)
{
int c = s[id][i] - 'a';
if (!tr[u][c])
tr[u][c] = ++tot;
u = tr[u][c];
}
e[u] = 1;
m[u] = id;
}
void build()
{
queue<int> q;
for (int i = 0; i < 26; ++i)
if (tr[0][i])
q.push(tr[0][i]);
while (q.size())
{
int u = q.front();
q.pop();
for (int i = 0; i < 26; ++i)
{
if (tr[u][i])
f[tr[u][i]] = tr[f[u]][i], q.push(tr[u][i]);
else
tr[u][i] = tr[f[u]][i];
}
}
}
void query(char *t, int n)
{
int u = 0;
for (int i = 0; t[i]; ++i)
{
u = tr[u][t[i] - 'a'];
for (int j = u; j; j = f[j])
{
if (e[j])
ans[m[j]]++;
}
}
int maxv = 0;
for (int i = 0; i < n; ++i)
maxv = max(maxv, ans[i]);
printf("%d\n", maxv);
for (int i = 0; i < n; ++i)
{
if (ans[i] == maxv)
printf("%s\n", s[i]);
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in3796", "r", stdin);
#endif
int n;
while (1)
{
scanf("%d", &n);
if (!n)
return 0;
char s[maxn];
ac::init();
for (int i = 0; i < n; ++i)
ac::insert(i);
ac::build();
scanf("%s", s);
ac::query(s, n);
}
}
P3966 单词,要求实现多模式串彼此匹配,思路大概是在trie图上跑反向广搜,根据上文提到的性质不难统计答案。,fail数组用于状态转移、统计答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ldb;
#define pb push_back
namespace ac
{
int tot;
const int N = 1e6 + 5;
int e[N], f[N], tr[N][26];
ll w[N];
int node[203];
void insert(char *s, int i)
{
int u = 0;
for (int i = 0; s[i]; ++i)
{
int c = s[i] - 'a';
if (!tr[u][c])
tr[u][c] = ++tot;
u = tr[u][c];
w[u]++;
}
node[i] = u;
}
stack<int> p;
void build()
{
queue<int> q;
for (int i = 0; i < 26; ++i)
if (tr[0][i])
q.push(tr[0][i]);
while (q.size())
{
int u = q.front();
q.pop();
p.push(u);
for (int i = 0; i < 26; ++i)
{
if (tr[u][i])
{
f[tr[u][i]] = tr[f[u]][i];
q.push(tr[u][i]);
}
else
tr[u][i] = tr[f[u]][i];
}
}
}
void search()
{
while (p.size())
{
int u = p.top();
p.pop();
w[f[u]] += w[u];
}
}
void print(int n)
{
for (int i = 0; i < n; ++i)
{
printf("%lld\n", w[node[i]]);
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in", "r", stdin);
#endif
int n;
char t[ac::N];
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%s", t);
ac::insert(t, i);
}
ac::build();
ac::search();
ac::print(n);
return 0;
}
还有一题,Video Game G,这个是ac自动机+DP,估计也是一个挺常见类型,还没A掉。