2021.09.01 自动机

2021.09.01 自动机

AC自动机

学习记录:

void build(){
	for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=0;i<26;i++)
		if(tr[x][i])
		fail[tr[x][i]]=tr[fail[x]][i],q.push(tr[x][i]);
		else tr[x][i]=tr[fail[x]][i];
		//通过else来简化一直转跳
		//相当于又给不存在的点和已经存在的序列一样的点合并 
	}
}
int query(char *t){
	int u=0,ans=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;
} 

重点:

1.fail的意义!

模板:

P3808 【模板】AC自动机(简单版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=1e6+10;

namespace ac{
	int tr[N][30],tot,e[N],fail[N];
	//fail数组是当前状态有相同状态的最长前缀 
	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];
	}
	queue<int>q;
	void build(){
		for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);
		while(!q.empty()){
			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],q.push(tr[u][i]);
				else tr[u][i]=tr[fail[u]][i];
			}
		}
	}
	int query(char *t){
		int u=0,ans=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])
			ans+=e[j],e[j]=-1; 
		}
		return ans;
	}
}

int n;
char s[N];

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

int main(){
	n=read();
	for(int i=1;i<=n;i++)scanf("%s",s+1),ac::insert(s);
	scanf("%s",s+1);
	ac::build();
	cout<<ac::query(s);
	return 0;
}

P3796 【模板】AC自动机(加强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

while (~scanf("%d%d",&n,&m))_笃行999-CSDN博客

while(scanf("%d",&n)!=EOF) 用法_笃行999-CSDN博客

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=16000;

namespace ac{
	int tr[N][30],tot,id[N],fail[N],val[N],cnt[200];
	//fail数组是当前状态有相同状态的最长前缀 
	void init(){
		tot=0;
		memset(fail,0,sizeof(fail));
		memset(tr,0,sizeof(tr));
		memset(val,0,sizeof(val));
		memset(id,0,sizeof(id));
		memset(cnt,0,sizeof(cnt));
	}
	void insert(char *s,int idi){
		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'];
		}
		id[u]=idi;
	}
	queue<int>q;
	void build(){
		for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);
		while(!q.empty()){
			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],q.push(tr[u][i]);
				else tr[u][i]=tr[fail[u]][i];
			}
		}
	}
	int query(char *t){
		int u=0,ans=0;
		for(int i=1;t[i];i++){
			u=tr[u][t[i]-'a'];
			for(int j=u;j;j=fail[j])++val[j];
		}
		for(int i=0;i<=tot;i++)
		if(id[i])ans=max(ans,val[i]),cnt[id[i]]=val[i];
		return ans;
	}
}

const int M=1e6+10;
int n;
char s[200][100],t[M];

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

int main(){
	while((n=read())&&n){
		ac::init();
		for(int i=1;i<=n;i++)scanf("%s",s[i]+1),ac::insert(s[i],i);
		ac::build();
		scanf("%s",t+1);
		int x=ac::query(t);
		cout<<x<<endl;
		for(int i=1;i<=n;i++)if(ac::cnt[i]==x)printf("%s\n",s[i]+1);
	}
	return 0;
}

P5357 【模板】AC自动机(二次加强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

重点:

1.strlen对于char * 无用,只能得出来0

shift!地址有个锤子的长度?!

2021.11.09 补充:有用,从0开始strlen(S),从1开始strlen(S+1)

[C++ 学习——char * ,char a ],char ** ,char *a[] 的区别_xiaohu的博客-CSDN博客

string、char、char*之间的转换

https://www.cnblogs.com/Pillar/p/4206452.html

2.memset对于struct有可能报错

3.AC自动机的优化形式

题解 P5357 【【模板】AC自动机(二次加强版)】 - hyfhaha 的博客 - 洛谷博客 (luogu.com.cn)

满分代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue> 
#include<cstring>
using namespace std;

const int N=2e6+10;
int n,tot,cnt[200010],ans,ru[N],fa[N];
char s[N],t[N];
struct node{
	int son[26],fail,flag,ans;
	void clear(){
		memset(son,0,sizeof(son));
		flag=fail=ans=0;
	}
}trie[N];
queue<int>q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
void insert(char *s,int idi){
	int u=0,len=strlen(s);
	//cout<<len<<endl;//
	for(int i=1;s[i];i++){
		if(trie[u].son[s[i]-'a']==0)trie[u].son[s[i]-'a']=++tot;//,
		//cout<<"u "<<u<<" s[i]-'a' "<<s[i]-'a'<<" tot "<<tot<<endl;
		u=trie[u].son[s[i]-'a'];
		//cout<<u<<" ";
	}
	if(!trie[u].flag)trie[u].flag=idi;
	fa[idi]=trie[u].flag;
	//cout<<endl;
	//cout<<idi<<" fa "<<fa[idi]<<" flag "<<trie[u].flag<<" u "<<u<<endl;
}
void build(){
	for(int i=0;i<26;i++)if(trie[0].son[i])q.push(trie[0].son[i]);
	while(!q.empty()){
		int x=q.front();q.pop();
		int faili=trie[x].fail;
		for(int i=0;i<26;i++){
			if(trie[x].son[i])
			trie[trie[x].son[i]].fail=trie[trie[x].fail].son[i],
			q.push((trie[x].son[i])),
			++ru[trie[trie[x].son[i]].fail];
			else trie[x].son[i]=trie[trie[x].fail].son[i];
		}
	}
}
void topu(){
	for(int i=0;i<=tot;i++)if(!ru[i])q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		cnt[trie[x].flag]=trie[x].ans;
		int faili=trie[x].fail;
		--ru[faili];
		trie[faili].ans+=trie[x].ans;
		if(!ru[faili])q.push(faili);
	}
}
void query(char *t){
	int u=0,len=strlen(t);
	for(int i=1;t[i];i++)
	u=trie[u].son[t[i]-'a'],++trie[u].ans;
}

int main(){
	//memset(&trie,0,sizeof(trie));
	n=read();
	for(int i=1;i<=n;i++)scanf("%s",s+1),insert(s,i);
	build();
	//for(int i=1;i<=n;i++)cout<<fa[i]<<" ";cout<<endl<<endl;
	scanf("%s",t+1);
	query(t);
	topu();
	for(int i=1;i<=n;i++)printf("%d\n",cnt[fa[i]]);
	return 0;
}

76分代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
using namespace std;

const int N=2e5+10;
int n,top;

namespace ac{
	int tr[N][30],tot,id[N],fail[N],val[N],cnt[N],fa[N];
	//fail数组是当前状态有相同状态的最长前缀 
	void init(){
		tot=0;
		memset(fail,0,sizeof(fail));
		memset(tr,0,sizeof(tr));
		memset(val,0,sizeof(val));
		memset(id,0,sizeof(id));
		memset(cnt,0,sizeof(cnt));
		//for(int i=1;i<=)
	}
	void insert(char *s,int idi){
		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'];
		}
		if(id[u])fa[idi]=id[u];
		else id[u]=idi;
	}
	queue<int>q;
	void build(){
		for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);
		while(!q.empty()){
			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],q.push(tr[u][i]);
				else tr[u][i]=tr[fail[u]][i];
			}
		}
	}
	void query(char *t){
		int u=0,ans=0;
		for(int i=1;t[i];i++){
			u=tr[u][t[i]-'a'];
			for(int j=u;j;j=fail[j])++val[j];
		}
		for(int i=0;i<=tot;i++)
		if(id[i])cnt[id[i]]=val[i];
		//return ans;
		for(int i=1;i<=n;i++){
			if(!fa[i])cout<<cnt[i]<<endl;
			else cout<<cnt[fa[i]]<<endl;
		}
	}
}

const int M=1e6+10;
char s[M],t[M*2];
map<string,int>mapi;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

int main(){
	n=read();
	//for(int i=1;i<=n;i++)ac::fa[i]=i;
	for(int i=1;i<=n;i++){
		ac::fa[i]=i;
		scanf("%s",s+1);
		ac::insert(s,i);
	}
	//for(int i=1;i<=n;i++)cout<<ac::fa[i]<<" ";
	ac::build();
	scanf("%s",t+1);
	ac::query(t);
	return 0;
}

fail树

[P2414 NOI2011] 阿狸的打字机 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

别人代码:

#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
const int MaxN = 1e5 + 10;

struct Trie {
    int Vis[30], End, Fail, Fa;
}Ac[MaxN];
int P = 1;

struct Edge {
    int To, Next;
}Road[MaxN];

struct Query {
    int X, Y;
};
int Last[MaxN], Cnt;

void Add(int U, int V) {
    Road[++Cnt] = (Edge) {V, Last[U]}, Last[U] = Cnt;
}

int Lowbit(int X) {
    return X & (-X);
}

int Tree[MaxN];
int N, M;

void Update(int X, int K) {
    while(X < MaxN) {
        Tree[X] += K;
        X += Lowbit(X);
    }
} 

int Sum(int X) {
    int Ans = 0;
    while(X) {
        Ans += Tree[X];
        X -= Lowbit(X);
    }
    return Ans;
}

char Or[MaxN], A[MaxN];
int Q[MaxN], Cur = 0, Ret = 0;

void Insert(char *S, int Root) {
    for(int i = 0; S[i]; i++) {
        if(S[i] == 'P') Q[++Ret] = Root; 
        else {
            if(S[i] == 'B') Root = Ac[Root].Fa;
            else {
                int Now = S[i] - 'a';
                if(!Ac[Root].Vis[Now]) Ac[Root].Vis[Now] = P, Ac[P].Fa = Root, P += 1;
                Root = Ac[Root].Vis[Now];
            }
        }
    Ac[Root].End = 1;
    }
}

void Build() {
    std::queue<int> Que;
    for(int i = 0; i < 26; i++) if(Ac[0].Vis[i]) Ac[Ac[0].Vis[i]].Fail = 0, Que.push(Ac[0].Vis[i]);
    while(!Que.empty()) {
        int Top = Que.front(); Que.pop();
        for(int i = 0; i < 26; i++) {
            int Vis = Ac[Top].Vis[i];
            if(Vis) {
                Ac[Vis].Fail = Ac[Ac[Top].Fail].Vis[i];
                Que.push(Ac[Top].Vis[i]);
            }
            else Ac[Top].Vis[i] = Ac[Ac[Top].Fail].Vis[i];
        }
    }
}

int Dfn[MaxN], Time = 0, R[MaxN];

void Dfs(int Now) {
    Dfn[Now] = ++Time;
    for(int i = Last[Now]; i; i = Road[i].Next) {
        int To = Road[i].To;
        Dfs(To);
    }
    R[Now] = Time;
}

int Tot[MaxN];
int Ans[MaxN];

int main() {
    scanf("%s", Or);
    Insert(Or, 0); Build();
    std::vector <Query> Ask[MaxN];
    for(int i = 1; i < P; i++) Add(Ac[i].Fail, i);
    Dfs(0); int Root = 0; scanf("%d", &N);
    for(int i = 0; i < N; i++) {
        int X, Y; scanf("%d%d", &X, &Y);
        Ask[Y].push_back((Query) {X, i});
    }
    Update(Dfn[0], 1);
    Ret = 0; 
    for(int i = 0; Or[i]; i++) {
        if(Or[i] == 'P') {
            Ret += 1;
            for(int j = 0; j < Ask[Ret].size(); j++) {
                int X = Q[Ask[Ret][j].X];
                Ans[Ask[Ret][j].Y] = Sum(R[X]) - Sum(Dfn[X] - 1);
            }
        }
        else if(Or[i] == 'B') Update(Dfn[Root], -1), Root = Ac[Root].Fa;
        else Root = Ac[Root].Vis[Or[i] - 'a'], Update(Dfn[Root], 1);
     }
    for(int i = 0; i < N; i++) printf("%d\n", Ans[i]);
} 
上一篇:pta甲级1008 Elevator(AC)


下一篇:2021.11.09 P4824 [USACO15FEB]Censoring S与P3121 [USACO15FEB]Censoring G(KMP&&AC自动机)