【总结】AC自动机处理的一类查询(bzo3881Divljak+bzoj2780Sevenk Love Oimaster+bzoj2754喵星球上的点名)

AC自动机

AC自动机是解决多模板匹配问题的算法。

它的优点在于思路易懂,代码简洁,可以在线性时间内求解。
缺点则在于必须要先知道所有的模板,而实际运用中很多情况无法预先知道需要查询的模板串。

AC自动机相关的题目有一些明显的特征:
文本串较长或文本串唯一/较少,多模式串匹配,可以离线,模式串总长有一定限制等。
而处理问题时也需要先离线建立所有模式串的AC自动机。

AC自动机常结合树(fail树)及dp(匹配方案/概率)考察。这篇总结主要写写关于fail树的一些操作。


匹配问题

串匹配的查询有以下几种基本形式:

文本串匹配

  1. 查询单个文本串中所有模式串出现的次数(同一个模式串出现多次算多次)

对于AC自动机的每个结点构造一个 e n d end end数组, e n d i end_i endi表示以结点 i i i结束的模式串个数。在 b f s bfs bfs构造 f a i l fail fail链时可以利用 e n d end end处理出 s u m sum sum数组, s u m i sum_i sumi表示结点 i i i f a i l fail fail树中到根结点路径上所有点的 e n d end end之和。

当文本串匹配到结点 i i i时,答案加上 s u m i sum_i sumi即可。复杂度为 O ( ∣ S ∣ + ∣ T ∣ ) O(|S|+|T|) O(S+T)
∣ S ∣ |S| S为文本串长, ∣ T ∣ |T| T为模式串总长。

  1. 查询单个文本串中出现的模式串的个数(同一个模式串出现多次算一次)

首先处理出 f a i l fail fail树的 d f s dfs dfs序。

考虑在1.中,每匹配到一个点就将答案加上 s u m sum sum,而其中会有些点被重复计算了多次,而算重的部分为一些点到根路径的共同路径。故将匹配过程中到达的所有结点取出,按 d f s dfs dfs序排序,加上每个点的 s u m sum sum,减去相邻两点 L C A LCA LCA s u m sum sum。复杂度多了个排序的 l o g log log

  1. 查询一组文本串(多个)中出现的模式串的个数(同一个模式串在这组文本串中出现多次算一次)

同2.,不过现在是在一组文本串匹配结束后,再取出结点排序处理。

模式串匹配

  1. 分别查询所有模式串在多个文本串中出现的次数(在一个文本串中出现多次算多次)

首先将所有模式串建立AC自动机并记录每个串对应的结束结点 p o s pos pos。处理出 f a i l fail fail树的 d f s dfs dfs序和每个结点的子树大小 s z sz sz

逐个将文本串插入AC自动机,每匹配到一个结点,将其值 v a l val val加1。可以发现一个串出现次数即为 f a i l fail fail树中其子树结点 v a l val val之和。

每个点子树在 d f s dfs dfs序中必然是连续的一段,答案区间求和,考虑构造BIT,可以在 l o g log log结点个数的时间求解。复杂度 O ( ( ∣ S ∣ + ∣ T ∣ ) l o g ∣ T ∣ ) O((|S|+|T|)log|T|) O((S+T)logT)

  1. 分别查询所有模式串在多个文本串中出现的次数(在一个文本串中出现多次算一次)

逐个插入文本串,同样考虑将每次匹配到的结点取出,按 d f s dfs dfs序排序,相邻两点 L C A LCA LCA v a l val val减1。复杂度还是 l o g log log的。

  1. 分别查询所有模式串在多组文本串中出现的次数(在一组文本串中出现多次算一次)

同2.,不过现在是在一组文本串匹配结束后,再取出结点排序处理。

下面三道题即为以上查询的简单应用。


bzoj3881:[Coci2015]Divljak

模式串匹配 2.

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;

int n,qr,df[N],top[N],f[N],sz[N],son[N],d[N];
int bit[N],pos[100010],dfn,pr;
int head[N],to[N<<1],nxt[N<<1],tot;

char s[N],SS[70];
queue<int>Q;

char cp;
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

inline void ot(int x)
{
	int re=0;
	for(;x;x/=10) SS[++re]='0'+x%10;
	if(!re) putchar('0');
	for(;re;--re) putchar(SS[re]);
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline bool cmp(const int&A,const int&B){return df[A]<df[B];}

void dfs(int x)
{
	int i,j;sz[x]=1;son[x]=-1;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[x]) continue;
		d[j]=d[x]+1;dfs(j);sz[x]+=sz[j];
		if((son[x]==-1) || sz[j]>sz[son[x]]) son[x]=j;
	}
}

void dfss(int x,int tpo)
{
	df[x]=++dfn;top[x]=tpo;
	if(son[x]==-1) return;
	dfss(son[x],tpo);
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[x] || j==son[x]) continue;
		dfss(j,j);
	}
}

inline int LCA(int x,int y)
{
    for(;top[x]!=top[y];x=f[top[x]]) 
      if(top[x]==0 || d[f[top[x]]]<d[f[top[y]]]) swap(x,y);
    if(d[x]<d[y]) swap(x,y);
	return y;
}

inline void modify(int x,int vv)
{for(;x<=dfn;x+=(x&(-x))) bit[x]+=vv;}

inline int query(int x)
{
	int re=0;
	for(;x;x-=(x&(-x))) 
	  re+=bit[x];
	return re;
}

inline void upp(int x)
{
	for(;~x;x=f[top[x]]){
		modify(df[top[x]],pr);
		if(df[x]<dfn) modify(df[x]+1,-pr);
	}
}

struct AC{
	queue<int>Q;
	int vs[N],cot,len,ch[N][26],cnt;
	
	inline void ins(int id,char *s)
	{
		int i,j,alp,u=0;len=strlen(s);
		for(i=0;i<len;++i){
			alp=s[i]-'a';
			if(!ch[u][alp]) ch[u][alp]=++cnt;
			u=ch[u][alp];
		}
		pos[id]=u;
	}
	
	void getfail()
	{
		int i,j,x,y,z,u;
		for(i=0;i<26;++i){
			x=ch[0][i];if(x) Q.push(x);
		}
		for(;!Q.empty();){
			x=Q.front();Q.pop();y=f[x];
			lk(x,y);lk(y,x);
			for(i=0;i<26;++i){
				z=ch[x][i];u=ch[y][i];
				if(!z) {ch[x][i]=u;continue;}
				f[z]=u;Q.push(z);
			}
		}
	}
	
	inline void ad(char *s)
    {
	   int i,j,alp,u=0,x;cot=0;len=strlen(s);
	   for(i=0;i<len;++i){
	      u=ch[u][s[i]-'a'];
	      vs[++cot]=u;
	   }
	   sort(vs+1,vs+cot+1,cmp);
	   cot=unique(vs+1,vs+cot+1)-vs-1;
	   pr=1;upp(vs[1]);
	   for(i=2;i<=cot;++i){
	   	 pr=1;upp(vs[i]);pr=-1;
			upp(LCA(vs[i-1],vs[i]));
	   }
    }
}ac;

int main(){
	int i,j,op,x;
	rd(n);f[0]=-1;
	for(i=1;i<=n;++i){scanf("%s",s);ac.ins(i,s);}
	ac.getfail();d[0]=1;dfs(0);dfss(0,0);
	rd(qr);
	for(;qr;--qr){
		rd(op);
		if(op==1){
			scanf("%s",s);ac.ad(s);
		}else{
			rd(x);
			ot(query(df[pos[x]]));putchar('\n');
		}
	}
	return 0;
}

bzoj2780[Spoj]8093 Sevenk Love Oimaster

模式串匹配 2.

#include<bits/stdc++.h>
using namespace std;
const int N=360010;

int n,qr,df[N],top[N],f[N],sz[N],son[N],d[N];
int bit[N],pos[60010],dfn;
int head[N],to[N<<1],nxt[N<<1],tot;

char t[N],SS[70];
string s[10010];

char cp;
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

inline void ot(int x)
{
	int re=0;
	for(;x;x/=10) SS[++re]='0'+x%10;
	if(!re) putchar('0');
	for(;re;--re) putchar(SS[re]);
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline bool cmp(const int&A,const int&B){return df[A]<df[B];}

void dfs(int x)
{
	int i,j;sz[x]=1;son[x]=-1;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[x]) continue;
		d[j]=d[x]+1;dfs(j);sz[x]+=sz[j];
		if((son[x]==-1) || sz[j]>sz[son[x]]) son[x]=j;
	}
}

void dfss(int x,int tpo)
{
	df[x]=++dfn;top[x]=tpo;
	if(son[x]==-1) return;
	dfss(son[x],tpo);
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[x] || j==son[x]) continue;
		dfss(j,j);
	}
}

inline int LCA(int x,int y)
{
    for(;top[x]!=top[y];x=f[top[x]]) 
      if(top[x]==0 || d[f[top[x]]]<d[f[top[y]]]) swap(x,y);
    if(d[x]<d[y]) swap(x,y);
	return y;
}

inline void modify(int x,int vv)
{for(;x<=dfn;x+=(x&(-x))) bit[x]+=vv;}

inline int query(int x)
{
	int re=0;
	for(;x;x-=(x&(-x))) 
	  re+=bit[x];
	return re;
}

struct AC{
	queue<int>Q;
	int vs[N],cot,len,ch[N][26],cnt;
	
	inline void ins(int id,char *s)
	{
		int i,j,alp,u=0;len=strlen(s);
		for(i=0;i<len;++i){
			alp=s[i]-'a';
			if(!ch[u][alp]) ch[u][alp]=++cnt;
			u=ch[u][alp];
		}
		pos[id]=u;
	}
	
	void getfail()
	{
		int i,j,x,y,z,u;
		for(i=0;i<26;++i){
			x=ch[0][i];if(x) Q.push(x);
		}
		for(;!Q.empty();){
			x=Q.front();Q.pop();y=f[x];
			lk(x,y);lk(y,x);
			for(i=0;i<26;++i){
				z=ch[x][i];u=ch[y][i];
				if(!z) {ch[x][i]=u;continue;}
				f[z]=u;Q.push(z);
			}
		}
	}
	
	inline void ad(string s)
    {
	   int i,j,alp,u=0,x;cot=0;
	   len=s.size();
	   for(i=0;i<len;++i){
	      u=ch[u][s[i]-'a'];
	      if(u) vs[++cot]=u;
	   }
	   if(!cot) return;
	   sort(vs+1,vs+cot+1,cmp);
	   cot=unique(vs+1,vs+cot+1)-vs-1;
	   modify(df[vs[1]],1);
	   for(i=2;i<=cot;++i){
	   	 modify(df[vs[i]],1);
		 modify(df[LCA(vs[i-1],vs[i])],-1);
	   }
    }
}ac;

int main(){
	int i,j,op,x;
	rd(n);rd(qr);
	for(i=1;i<=n;++i) cin>>s[i];
	for(i=1;i<=qr;++i){scanf("%s",t);ac.ins(i,t);}
	ac.getfail();
	d[0]=1;f[0]=-1;dfs(0);dfss(0,0);
	for(i=1;i<=n;++i) ac.ad(s[i]);
	for(i=1;i<=qr;++i){
		x=pos[i];
		ot((query(df[x]+sz[x]-1)-query(df[x]-1)));
		putchar('\n');
	}
	return 0;
}

bzoj2754[SCOI2012]喵星球上的点名

模式串匹配 3. + 文本串匹配 3.

#include<bits/stdc++.h>
#define itr map<int,int>::iterator
#define fi first
#define sc second
using namespace std;
const int N=1e5+10;

int n,m,df[N],top[N],f[N],sz[N],son[N],d[N];
int bit[N],pos[N],ans[50005],dfn,nwss;
int ed[N],sum[N];
int head[N],to[N<<1],nxt[N<<1],tot;

vector<int>nam[50005][2];

char cp,SS[70];
inline int rd()
{
	cp=getchar();int x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
    return x;
}

inline void ot(int x)
{
	int re=0;
	for(;x;x/=10) SS[++re]='0'+x%10;
	if(!re) putchar('0');
	for(;re;--re) putchar(SS[re]);
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline bool cmp(const int&A,const int&B){return df[A]<df[B];}

void dfs(int x)
{
	int i,j;sz[x]=1;son[x]=-1;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[x]) continue;
		d[j]=d[x]+1;sum[j]=sum[x]+ed[j];dfs(j);sz[x]+=sz[j];
		if((son[x]==-1) || sz[j]>sz[son[x]]) son[x]=j;
	}
}

void dfss(int x,int tpo)
{
	df[x]=++dfn;top[x]=tpo;
	if(son[x]==-1) return;
	dfss(son[x],tpo);
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[x] || j==son[x]) continue;
		dfss(j,j);
	}
}

inline int LCA(int x,int y)
{
    for(;top[x]!=top[y];x=f[top[x]]) 
      if(top[x]==0 || d[f[top[x]]]<d[f[top[y]]]) swap(x,y);
    if(d[x]<d[y]) swap(x,y);
	return y;
}

inline void modify(int x,int vv)
{for(;x<=dfn;x+=(x&(-x))) bit[x]+=vv;}

inline int query(int x)
{
	int re=0;
	for(;x;x-=(x&(-x))) re+=bit[x];
	return re;
}

struct AC{
	queue<int>Q;
	map<int,int>ch[N];
	int vs[N<<1],cot,len,cnt;
	
	inline void ins(int id)
	{
		int alp,u=0;
		for(len=rd();len;--len){
			alp=rd();
			if(!ch[u][alp]) ch[u][alp]=++cnt,u=cnt;
			else u=ch[u][alp];
		}
		ed[u]++;pos[id]=u;
	}
	
	void getfail()
	{
		int x,y,z,v,u;itr i;
		for(i=ch[0].begin();i!=ch[0].end();++i) Q.push(i->sc);
		for(;!Q.empty();){
			x=Q.front();Q.pop();y=f[x];
			lk(x,y);lk(y,x);
			for(i=ch[x].begin();i!=ch[x].end();++i){
				for(u=i->fi,z=y;z && (!ch[z][u]);z=f[z]);
                v=ch[z][u];f[i->sc]=v;Q.push(i->sc);
			}
		}
	}
	
	inline void cal(int id)
    {
	   int i,j,alp,u=0,z;nwss=cot=0;
	   for(j=0;j<2;++j){
	   	  u=0;
	   	  for(len=nam[id][j].size(),i=0;i<len;++i){
	   	     alp=nam[id][j][i];
	   	     if(!ch[u][alp]){
	   	   	    for(z=f[u];z && (!ch[z][alp]);z=f[z]);
	   	   	    u=ch[z][alp];
	   	     }else u=ch[u][alp];
	   	     if(u) vs[++cot]=u;
	     }
	   }
	   if(!cot) return;
	   sort(vs+1,vs+cot+1,cmp);
	   cot=unique(vs+1,vs+cot+1)-vs-1;
	   modify(df[vs[1]],1);nwss+=sum[vs[1]];
	   for(i=2;i<=cot;++i){
	   	 z=LCA(vs[i-1],vs[i]);
		 nwss-=sum[z];nwss+=sum[vs[i]];
	   	 modify(df[vs[i]],1);modify(df[z],-1);
	   }
	   ans[id]=nwss;
    }
}ac;

int main(){
	int i,j,x;
	n=rd();m=rd();
    for(i=1;i<=n;++i)
    	for(j=0;j<2;++j)
    		for(x=rd();x;--x)
    			nam[i][j].push_back(rd());
    for(i=1;i<=m;++i) ac.ins(i);
    ac.getfail();
	d[0]=1;f[0]=-1;dfs(0);dfss(0,0);
    for(f[0]=0,i=1;i<=n;++i) ac.cal(i);
	for(i=1;i<=m;++i){
		x=pos[i];
		ot(query(df[x]+sz[x]-1)-query(df[x]-1));
		putchar('\n');
	}
    for(i=1;i<=n;++i) 
	 ot(ans[i]),putchar(i==n?'\n':' ');
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值