AC自动机(Aho-Corasick algorithm)是一种多模式字符串匹配算法。它可以快速地查找多个模式串在一段文本串中出现的位置,并支持模式串的预处理,使得在查询时能够快速地匹配。
C++代码实现:
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 100005; //最大节点数
const int MAXM = 26; //最大字符集大小
struct Trie_Node //Trie树节点
{
int id; //节点编号
int nxt[MAXM]; //子节点
int fail; //失败指针
int val; //节点的输出值
}trie[MAXN];
int idx; //Trie树节点计数器
void trie_clear() //清空Trie树
{
memset(trie, 0, sizeof(trie));
idx = 0;
}
void trie_insert(char *s, int val) //往Trie树中插入一个字符串
{
int len = strlen(s);
int u = 0;
for(int i = 0; i < len; i++)
{
int c = s[i] - 'a';
if(!trie[u].nxt[c]) //新建节点
trie[u].nxt[c] = ++idx;
u = trie[u].nxt[c];
}
trie[u].val = val; //该节点的输出值为该字符串的权值
}
void ac_build() //构建AC自动机
{
queue<int> q;
for(int c = 0; c < MAXM; c++)
{
if(trie[0].nxt[c]) //所有的根节点的子节点的失败指针指向根节点
{
trie[trie[0].nxt[c]].fail = 0;
q.push(trie[0].nxt[c]);
}
}
while(!q.empty()) //BFS求出AC自动机的失败指针
{
int u = q.front();
q.pop();
for(int c = 0; c < MAXM; c++)
{
int v = trie[u].nxt[c];
if(v)
{
trie[v].fail = trie[trie[u].fail].nxt[c]; //与父节点的失败指针相同分两种情况
if(trie[trie[v].fail].val) //如果该节点的失败指针是可接受节点,那么当前节点就是可接受节点的后缀
trie[v].val = trie[trie[v].fail].val; //更新该节点的输出值
q.push(v);
}
else
{
trie[u].nxt[c] = trie[trie[u].fail].nxt[c];
}
}
}
}
char s[MAXN]; //输入的文本串
int pos[MAXN]; //所有模式串在文本串中的位置
bool vis[MAXN]; //标记每个出现过的节点
int ans; //输出值的和
void ac_query() //在Trie树上匹配文本串
{
int u = 0;
int len = strlen(s);
for(int i = 0; i < len; i++) //从根节点开始匹配
{
int c = s[i] - 'a';
u = trie[u].nxt[c]; //转移到下一个节点
int v = u;
while(v) //利用失败指针更新所有可接受的节点
{
if(trie[v].val && !vis[v])
{
vis[v] = true;
ans += trie[v].val;
}
v = trie[v].fail;
}
}
}
int main()
{
int n;
while(cin >> n)
{
trie_clear();
for(int i = 1; i <= n; i++)
{
int val;
cin >> s >> val;
trie_insert(s, val);
}
ac_build();
cin >> s;
memset(vis, false, sizeof(vis));
ans = 0;
ac_query();
cout << ans << endl;
}
return 0;
}
过程讲解:
- 定义Trie树节点
struct Trie_Node //Trie树节点
{
int id; //节点编号
int nxt[MAXM]; //子节点
int fail; //失败指针
int val; //节点的输出值
};
- 往Trie树中插入一个字符串
void trie_insert(char *s, int val) //往Trie树中插入一个字符串
{
int len = strlen(s);
int u = 0;
for(int i = 0; i < len; i++)
{
int c = s[i] - 'a';
if(!trie[u].nxt[c]) //新建节点
trie[u].nxt[c] = ++idx;
u = trie[u].nxt[c];
}
trie[u].val = val; //该节点的输出值为该字符串的权值
}
- 构建AC自动机
void ac_build() //构建AC自动机
{
queue<int> q;
for(int c = 0; c < MAXM; c++)
{
if(trie[0].nxt[c]) //所有的根节点的子节点的失败指针指向根节点
{
trie[trie[0].nxt[c]].fail = 0;
q.push(trie[0].nxt[c]);
}
}
while(!q.empty()) //BFS求出AC自动机的失败指针
{
int u = q.front();
q.pop();
for(int c = 0; c < MAXM; c++)
{
int v = trie[u].nxt[c];
if(v)
{
trie[v].fail = trie[trie[u].fail].nxt[c]; //与父节点的失败指针相同分两种情况
if(trie[trie[v].fail].val) //如果该节点的失败指针是可接受节点,那么当前节点就是可接受节点的后缀
trie[v].val = trie[trie[v].fail].val; //更新该节点的输出值
q.push(v);
}
else
{
trie[u].nxt[c] = trie[trie[u].fail].nxt[c];
}
}
}
}
- 在Trie树上匹配文本串
void ac_query() //在Trie树上匹配文本串
{
int u = 0;
int len = strlen(s);
for(int i = 0; i < len; i++) //从根节点开始匹配
{
int c = s[i] - 'a';
u = trie[u].nxt[c]; //转移到下一个节点
int v = u;
while(v) //利用失败指针更新所有可接受的节点
{
if(trie[v].val && !vis[v])
{
vis[v] = true;
ans += trie[v].val;
}
v = trie[v].fail;
}
}
}
最后,只需调用以上函数即可。
标签:nxt,AC,val,trie,C++,int,fail,自动机,节点 From: https://www.cnblogs.com/oiercc/p/17339269.html