AC自动机

AC 自动机 - OI Wiki (oi-wiki.org)

给定一个长度为m主串再给出n个平均长度为w模式串问这些模式串分别出现了多少次。

如果对n个模式串分别进行kmp算法那么时间复杂度:n次匹配 每次(m+w) 所以是O(nm+nw)

ac自动机时间复杂度: 建树O(w*n) 建立fail数组 O(w*n) 匹配O(w*m) 所以是O(wm+nw)

所以可知当n相对于w很大即模式串的数量较模式串的平均长度很大时就应该用AC自动机来解决

对于字典树我们知道,字典树是用主串建树,然后对于各个模式串分别在主串上面跑一遍,而ac自动机是对于模式串建树,然后把主串在模式串上面跑一遍。

AC自动机算法分为3步:构造一棵Trie树,构造失败指针和模式匹配过程。
如果你对KMP算法和了解的话,应该知道KMP算法中的next函数(shift函数或者fail函数)是干什么用的。KMP中我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符,当A[i+1]≠B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配,而next函数恰恰记录了这个j应该调整到的位置。同样AC自动机的失败指针具有同样的功能,也就是说当我们的模式串在Tire上进行匹配时,如果与当前节点的关键字不能继续匹配的时候,就应该去当前节点的失败指针所指向的节点继续进行匹配。

举例:

以要查找的单词建树:say he shr her  

初始化

const int N = 1e6 + 6;
int n;
char s[N];
namespace AC {
int tr[N][26], tot;
int e[N], fail[N];

建树

void insert(char *s) {
  int u = 0;
  for (int i = 1; s[i]; i++) {
    if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++tot;  //如果没有则插入新节点
    u = tr[u][s[i] - 'a'];                              //搜索下一个节点
  }
  e[u]++;  //尾为节点 u 的串的个数
}

构造失败指针

queue<int>q;
void build() {
  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]) {
        fail[tr[u][i]] =
tr[fail[u]][i];  // fail数组:同一字符可以匹配的其他位置 这里的tr[fail[u][i]]就是所求的最长后缀
        q.push(tr[u][i]);
      } else
        tr[u][i] = tr[fail[u]][i];
    }
  }
}

fail[i]=j 说明以i为终止节点的单词的最长后缀是以j为终止节点的单词,所以fail[u]表示当前主串匹配到了u这个位置,那么fail[u]这个位置也一定匹配成功了,如匹配到了she的e,那么he也一定匹配成功了

匹配

这里  作为字典树上当前匹配到的结点,res 即返回的答案。循环遍历匹配串, 在字典树上跟踪当前字符。利用 fail 指针找出所有匹配的模式串,累加到答案中。然后清零。在上文中我们分析过,字典树的结构其实就是一个 trans 函数,而构建好这个函数后,在匹配字符串的过程中,我们会舍弃部分前缀达到最低限度的匹配。fail 指针则指向了更多的匹配状态。最后上一份图。对于刚才的自动机:

int query(char *t) {
  int u = 0, res = 0;
  for (int i = 1; t[i]; i++) {
    u = tr[u][t[i] - 'a'];  // 转移
    for (int j = u; j && e[j] != -1; j = fail[j]) {
      res += e[j], e[j] = -1;
    }
  }
  return res;
}
}  

hdu3065 链接

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int u=0;
int tree[maxn][26];
int fail[maxn];
int e[maxn];
map<int,string>mp;
map<string,int>check;
vector<string>vec;
void build(char s[]){
	int len=strlen(s);
	int h=0;
	for(int i=0;i<len;i++){
		int now=s[i]-'A';
		if(!tree[h][now]){
			u++;
			tree[h][now]=u;
		}
		h=tree[h][now];
	}
	e[h]++;
	mp[h]=s;
}
queue<int>q;
void build_fail(){
	for(int i=0;i<26;i++){
		if(tree[0][i]){
			q.push(tree[0][i]);
		}
	}
	while(q.size()){
		int now=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			if(tree[now][i]){
				fail[tree[now][i]]=tree[fail[now]][i];
				q.push(tree[now][i]);
			}
			else{
				tree[now][i]=tree[fail[now]][i];
			}
		}
	}
}
void find(string s){
    int u=0;int res=0;
	for(int i=0;i<s.size();i++){
		u=tree[u][s[i]-'A'];
		int j;
		for(j=u;j;j=fail[j]){
			check[mp[j]]+=e[j];
		}
	}
}

int main(){
	int n;
	cin>>n;
	while(n--){
		char s[55];
		cin>>s;
		vec.push_back(s);
		build(s);
	}
	build_fail();
	string tmp;cin>>tmp;
	find(tmp);
	for(int i=0;i<vec.size();i++){
		if(check[vec[i]]){
			cout<<vec[i]<<": "<<check[vec[i]]<<endl;
		}
	}
} 

AC自动机的应用

例题

https://www.acwing.com/problem/content/submission/code_detail/15098046/

对于各个治病DNA片段可以当作是模式串,对于模式串建立ac自动机,然后对于主串进行修改,看最少要修改多少个位置可以(让主串与trie树没有匹配使得到了u这个模式的时候,u的后缀含有治病DNA片段),dp解决

#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int maxn=4e3+5;
const int inf=1e9+7;
int n;
int tot;
int cnt=0;
int tree[maxn][4];
int fail[maxn];
int st[maxn];
map<char,int>mp;
map<int,char>mpp;
string s;
int check[maxn][1005];
void insert(string s){
	int u=0;
	for(int i=0;i<s.size();i++){
		int now=mp[s[i]];
		if(!tree[u][now]){
			tree[u][now]=++cnt;
		}
		u=tree[u][now];
	}
	st[u]=1;
}
void build_fail(){
	int u=0;
	queue<int>q;
	for(int i=0;i<4;i++){
		if(tree[u][i]){
			q.push(tree[u][i]);
		}
	}
	while(q.size()){
		int now=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			if(tree[now][i]){
				fail[tree[now][i]]=tree[fail[now]][i];
				if(st[fail[tree[now][i]]]){
					st[tree[now][i]]=1;
				}
				q.push(tree[now][i]);
			}
			else{
				tree[now][i]=tree[fail[now]][i];
			}
		}
	}
}
int dp(int u,int len){
	if(len==s.size()){
		return check[u][len]=0;
	}
	if(check[u][len]!=-1){
		return check[u][len];
	}
	int res=inf;
	for(int i=0;i<4;i++){
		if(!st[tree[u][i]]){
			res=min(res,dp(tree[u][i],len+1)+(mpp[i]!=s[len]));
		}
	}
	return check[u][len]=res;
}
void solve(){
	memset(tree,0,sizeof(tree));
	memset(st,0,sizeof(st));
	memset(check,-1,sizeof(check));
	memset(fail,0,sizeof(fail));
	cnt=0;
	while(n--){
		cin>>s;
		insert(s);	
	}
	build_fail();
	cin>>s;
	int ans=dp(0,0);
	if(ans>=1e9){
		ans=-1;
	}
	cout<<"Case "<<tot<<": "<<ans<<"\n";
}
int main(){	
	mp['A']=0;
	mp['G']=1;
	mp['C']=2;
	mp['T']=3;
	mpp[0]='A';
	mpp[1]='G';
	mpp[2]='C';
	mpp[3]='T';
	while(cin>>n){
		if(n==0){
			break;
		}
		tot++;
		solve();
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python AC自动机是一个用于字符串匹配的算法,它可以高效地在一段文本中查找多个预定义的模式。它的实现可以使用多种库,其中包括ac自动机python和ahocorasick-python。 ac自动机python是一个对标准的ac自动机算法进行了完善和优化的实现,适用于主流的Python发行版,包括Python2和Python3。它提供了更准确的结果,并且可以通过pip进行安装,具体的安装方法可以参考官方文档或者使用pip install命令进行安装。 ahocorasick-python是另一个实现AC自动机的库,它也可以用于Python2和Python3。你可以通过官方网站或者GitHub源码获取更多关于该库的信息和安装指南。 对于AC自动机的使用,一个常见的例子是在一段包含m个字符的文章中查找n个单词出现的次数。要了解AC自动机,需要有关于模式树(字典树)Trie和KMP模式匹配算法的基础知识。AC自动机算法包括三个步骤:构造一棵Trie树,构造失败指针和模式匹配过程。在构造好AC自动机后,可以使用它来快速地在文本中查找预定义的模式,并统计它们的出现次数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ahocorasick-python:AC自动机python的实现,并进行了优化。 主要修复了 查询不准确的问题](https://download.csdn.net/download/weixin_42122986/18825869)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Python实现多模匹配——AC自动机](https://blog.csdn.net/zichen_ziqi/article/details/104246446)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值