:D 正在获取一言...

ac自动机

ac自动机(可排相同字符字符串)

点击查看代码
//ac自动机
#include<bits/stdc++.h>
#define maxn 2000001
using namespace std;
char s[maxn],T[maxn];
int n,cnt,vis[200051],ans,in[maxn],Map[maxn];
struct kkk{
	int son[26],fail,flag,ans;
}trie[maxn];
queue<int>q;
void insert(char* s,int num){
	int u=1,len=strlen(s);
	for(int i=0;i<len;++i){
		int v=s[i]-'a';
		if(!trie[u].son[v])trie[u].son[v]=++cnt;
		u=trie[u].son[v];
	}
	if(!trie[u].flag)trie[u].flag=num;
	Map[num]=trie[u].flag;
}
void getFail(){
	for(int i=0;i<26;i++)trie[0].son[i]=1;
	q.push(1);
	while(!q.empty()){
		int u=q.front();q.pop();
		int Fail=trie[u].fail;
		for(int i=0;i<26;++i){
			int v=trie[u].son[i];
			if(!v){trie[u].son[i]=trie[Fail].son[i];continue;}
			trie[v].fail=trie[Fail].son[i]; in[trie[v].fail]++;
			q.push(v);
		}
	}
}
void topu(){
	for(int i=1;i<=cnt;++i)
	if(in[i]==0)q.push(i);				//将入度为0的点全部压入队列里
	while(!q.empty()){
		int u=q.front();q.pop();vis[trie[u].flag]=trie[u].ans;	//如果有flag标记就更新vis数组
		int v=trie[u].fail;in[v]--;		//将唯一连出去的出边fail的入度减去(拓扑排序的操作)
		trie[v].ans+=trie[u].ans;		//更新fail的ans值
		if(in[v]==0)q.push(v);			//拓扑排序常规操作
	}
}
void query(char* s){
	int u=1,len=strlen(s);
	for(int i=0;i<len;++i)
	u=trie[u].son[s[i]-'a'],trie[u].ans++;
}
int main(){
	scanf("%d",&n); cnt=1;
	for(int i=1;i<=n;++i){
		scanf("%s",s);
		insert(s,i);
	}getFail();scanf("%s",T);
	query(T);topu();
	for(int i=1;i<=n;++i)printf("%d\n",vis[Map[i]]);
}

ac自动机普通版

点击查看代码
#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
struct kkk{
	int son[26],flag,fail;
}trie[maxn];
int n,cnt;
char s[1000001];
queue<int >q;
void insert(char* s){
	int u=1,len=strlen(s);
	for(int i=0;i<len;i++){
		int v=s[i]-'a';
		if(!trie[u].son[v])trie[u].son[v]=++cnt;
		u=trie[u].son[v];
	}
	trie[u].flag++;
}
void getFail(){
	for(int i=0;i<26;i++)trie[0].son[i]=1;			//初始化0的所有儿子都是1
	q.push(1);trie[1].fail=0;				//将根压入队列
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;i++){				//遍历所有儿子
			int v=trie[u].son[i];			//处理u的i儿子的fail,这样就可以不用记父亲了
			int Fail=trie[u].fail;			//就是fafail,trie[Fail].son[i]就是和v值相同的点
			if(!v){trie[u].son[i]=trie[Fail].son[i];continue;}	//不存在该节点,第二种情况
			trie[v].fail=trie[Fail].son[i];	//第三种情况,直接指就可以了
			q.push(v);						//存在实节点才压入队列
		}
	}
}
int query(char* s){
	int u=1,ans=0,len=strlen(s);
	for(int i=0;i<len;i++){
		int v=s[i]-'a';
		int k=trie[u].son[v];		//跳Fail
		while(k>1&&trie[k].flag!=-1){	//经过就不统计了
			ans+=trie[k].flag,trie[k].flag=-1;	//累加上这个位置的模式串个数,标记已经过
			k=trie[k].fail;			//继续跳Fail
		}
		u=trie[u].son[v];			//到下一个儿子
	}
	return ans;
}
int main(){
	cnt=1;            //代码实现细节,编号从1开始
        scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		insert(s);
	}
	getFail();
	scanf("%s",s);
	printf("%d\n",query(s));
	return 0;
}

ac自动机速度强化版

点击查看代码
#include<bits/stdc++.h>
#define maxn 2000001
using namespace std;
char s[maxn],T[maxn];
int n,cnt,vis[200051],ans,in[maxn],Map[maxn];
struct kkk{
	int son[26],fail,flag,ans;
}trie[maxn];
queue<int>q;
void insert(char* s,int num){
	int u=1,len=strlen(s);
	for(int i=0;i<len;++i){
		int v=s[i]-'a';
		if(!trie[u].son[v])trie[u].son[v]=++cnt;
		u=trie[u].son[v];
	}
	if(!trie[u].flag)trie[u].flag=num;
	Map[num]=trie[u].flag;
}
void getFail(){
	for(int i=0;i<26;i++)trie[0].son[i]=1;
	q.push(1);
	while(!q.empty()){
		int u=q.front();q.pop();
		int Fail=trie[u].fail;
		for(int i=0;i<26;++i){
			int v=trie[u].son[i];
			if(!v){trie[u].son[i]=trie[Fail].son[i];continue;}
			trie[v].fail=trie[Fail].son[i]; in[trie[v].fail]++;
			q.push(v);
		}
	}
}
void topu(){
	for(int i=1;i<=cnt;++i)
	if(in[i]==0)q.push(i);				//将入度为0的点全部压入队列里
	while(!q.empty()){
		int u=q.front();q.pop();vis[trie[u].flag]=trie[u].ans;	//如果有flag标记就更新vis数组
		int v=trie[u].fail;in[v]--;		//将唯一连出去的出边fail的入度减去(拓扑排序的操作)
		trie[v].ans+=trie[u].ans;		//更新fail的ans值
		if(in[v]==0)q.push(v);			//拓扑排序常规操作
	}
}
void query(char* s){
	int u=1,len=strlen(s);
	for(int i=0;i<len;++i)
	u=trie[u].son[s[i]-'a'],trie[u].ans++;
}
int main(){
	scanf("%d",&n); cnt=1;
	for(int i=1;i<=n;++i){
		scanf("%s",s);
		insert(s,i);
	}getFail();scanf("%s",T);
	query(T);topu();
	for(int i=1;i<=n;++i)printf("%d\n",vis[Map[i]]);
}

posted @ 2025-08-29 11:37  呱唧呱唧呱呱呱  阅读(13)  评论(0)    收藏  举报