【模版】AC自动机


【模版】AC自动机


可以在文本串中查找多个模式串。
前置知识点:
KMP 算法
Trie 树


开了一个 f a i l [ i ] fail[i] fail[i] 数组(存的是 t r i e trie trie 树上的结点号)
表示 t r i e trie trie 树 某个结点 在 文本串上 第 t r i e [ i ] [ j ] trie[i][j] trie[i][j] 号节点匹配上时,跳至 与当前所在 模式串 的后缀有 公共前缀的 下一个 模式串 。跳至的目标就是 f a i l [ i ] fail[i] fail[i]
比如两个模式串 a b c d abcd abcd a b c abc abc 。 在访问 a b c d abcd abcd 时的 c c c 时,会跳去判断第二个字符串 a b c abc abc 因为 a b c d abcd abcd 前三个字母 是 a b c abc abc 的后三个字符,符合前缀后缀相同的关系。


T r i e Trie Trie 树的建立没有变化,不过建立的是模式串的 T r i e Trie Trie 树 。
f a i l [ ] fail[] fail[] 数组需要提前预处理:
按层数对 t r i e trie trie 树进行 b f s bfs bfs , 对于每一个被遍历到点(无论是否存在子节点),首先判断其子节点是否是另一个模式串的前缀部分。
如果是(则说明其有子结点),则其子节点的 f a i l [ ] fail[] fail[] 数组 保存 与该模式串的后缀 有 相同前缀的 结点编号。
如果否(则说明其无子节点),则 令其 新创立 t r i e [ i ] [ j ] trie[i][j] trie[i][j], 保存当前点 的 f a i l [ ] fail[] fail[] 数组 所记录点的 子结点。


void Set_fail()
{
	for(int i = 0; i < 26; i++)	
		if(trie[0][i])
			q.push(trie[0][i]);
	while(!q.empty())
	{
		int k = q.front();
		q.pop();
		for(int i = 0; i < 26; i++)
			if(trie[k][i])
			{
				fail[trie[k][i]] = trie[fail[k]][i];
				q.push(trie[k][i]);
			}
			else	trie[k][i] = trie[fail[k]][i];
	}
}

当在文本串上查询模式串的时候,每当遍历到一个结点,跳至其 f a i l [ ] fail[] fail[] 结点,如果某一模式串以该结点为结尾,则匹配成功。

int Query(char st[])
{
	int k = 0, ans = 0, len = strlen(st);
	for(int i = 0; i < len; i++)
	{
		k = trie[k][st[i]-'a'];
		for(int j = k; j && rt[j] != -1; j = fail[j])
			ans += rt[j], rt[j] = -1;
	}
	return ans;
}

例题:
AC自动机[简单]
代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
const int Maxn = 1000100;
queue <int> q;
int n,cnt;
char t[Maxn];
int trie[Maxn][27], fail[Maxn], rt[Maxn];

void Insert(char st[])
{
	int len = strlen(st), k = 0;
	for(int i = 0; i < len; i++)
	{
		int c = st[i] - 'a';
		if(!trie[k][c])	trie[k][c] = ++cnt;
		k = trie[k][c];
	}
	rt[k]++;
}

void Set_fail()
{
	for(int i = 0; i < 26; i++)	
		if(trie[0][i])
			q.push(trie[0][i]);
	while(!q.empty())
	{
		int k = q.front();
		q.pop();
		for(int i = 0; i < 26; i++)
			if(trie[k][i])
			{
				fail[trie[k][i]] = trie[fail[k]][i];
				q.push(trie[k][i]);
			}
			else	trie[k][i] = trie[fail[k]][i];
	}
}

int Query(char st[])
{
	int k = 0, ans = 0, len = strlen(st);
	for(int i = 0; i < len; i++)
	{
		k = trie[k][st[i]-'a'];
		for(int j = k; j && rt[j] != -1; j = fail[j])
			ans += rt[j], rt[j] = -1;
	}
	return ans;
}

int main()
{
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%s",t);
		t[strlen(t)] = '\0';
		Insert(t);
	}
	Set_fail();
	scanf("%s",t);
	t[strlen(t)] = '\0';
	printf("%d\n",Query(t));
	return 0;
}

AC自动机[加强]
代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <stack>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
const int Maxn = 12000;
const int Maxs = 70 + 10;
queue <int> q;
int n, cnt;
int trie[Maxn][30], fail[Maxn], rt[Maxn], app[Maxn];
char t[160][Maxs], key[1000100];

void Insert(char st[], int id)
{
    int len = strlen(st), k = 0;
    for(int i = 0; i < len; i++)
    {
        int c = st[i] - 'a';
        if(!trie[k][c]) trie[k][c] = ++cnt;
        k = trie[k][c];
    }
    rt[k] = id;
}
void Set_fail(char st[])
{
    int len = strlen(st), k = 0;
    for(int i = 0; i < 26; i++)
        if(trie[0][i])
            q.push(trie[0][i]);
    while(!q.empty())
    {
        int k = q.front();
        q.pop();
        for(int i = 0; i < 26; i++)
            if(trie[k][i])
            {
                fail[trie[k][i]] = trie[fail[k]][i];
                q.push(trie[k][i]);
            }
            else    trie[k][i] = trie[fail[k]][i];
    }
}
void Pre_work(char st[])
{
    int len = strlen(st), k = 0;
    for(int i = 0; i < len; i++)
    {
        k = trie[k][st[i]-'a'];
        for(int j = k; j; j = fail[j])
            if(rt[j])
                app[rt[j]]++;
    }
}

int main()
{
    scanf("%d",&n);
    while(n)
    {
        cnt = 0;
        memset(trie, 0, sizeof(trie));
        memset(fail, 0, sizeof(fail));
        memset(app, 0, sizeof(app));
        memset(rt, 0, sizeof(rt));
        for(int i = 1; i <= n; i++)
        {
            scanf("%s",t[i]);
            t[i][strlen(t[i])] = '\0';
            Insert(t[i], i);
        }
        scanf("%s",key);
        Set_fail(key);
        Pre_work(key);
        int ans = 0;
        for(int i = 1; i <= n; i++) ans = max(ans, app[i]);
        printf("%d\n",ans);
        for(int i = 1; i <= n; i++) if(app[i] == ans)   printf("%s\n",t[i]);
        cin >> n;
    }
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值