ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【CF547E】Mike and Friends(AC自动机)

2021-04-03 08:01:09  阅读:236  来源: 互联网

标签:AC int CF547E CI lnk 自动机 Friends define


点此看题面

  • 给定\(n\)个字符串,\(q\)次询问,每次求\(s_k\)在\(s_{l\sim r}\)中的出现次数总和。
  • \(\sum|s|\le2\times10^5,q\le5\times10^5\)

\(AC\)自动机+树状数组

这种一堆字符串的题目,要么建\(AC\)自动机,要么建广义后缀自动机,其实这题两者都行。

最后我还是选择了\(AC\)自动机(主要太久没写过了)。

考虑把询问离线,变成\(s_{1\sim r}\)中\(s_k\)的出现次数减去\(s_{1\sim l-1}\)中\(s_k\)的出现次数。

先离线建出\(AC\)自动机,然后就变成一个不断往里面加串然后做全局询问的问题。

众所周知子串是前缀的后缀,所以我们加入一个串时对于每个前缀打上标记,对应的后缀就是\(fail\)树上的列祖列宗。

因此,询问的时候只要询问这个串在\(AC\)自动机上对应节点子树内的标记总和。

单点修改,子树求和,把\(fail\)树的\(dfs\)序列求出来用树状数组维护即可。

代码:\(O(qlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define M 500000
using namespace std;
int n,bg[N+5],l[N+5],ans[M+5];char s[N+5];
struct Q {int p,k,op;I Q(CI i=0,CI x=0,CI f=0):p(i),k(x),op(f){}};vector<Q> q[N+5];vector<Q>::iterator it;
namespace T//fail树
{
	int d,dI[N+5],dO[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N+5];
	I void Add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}//加边
	I void Init(CI x) {dI[x]=++d;for(RI i=lnk[x];i;i=e[i].nxt) Init(e[i].to);dO[x]=d;}//求出dfs序列
}
struct TreeArray
{
	int a[N+5];I void U(RI x) {W(x<=T::d) ++a[x],x+=x&-x;}I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}//树状数组
}A;
namespace AC//AC自动机
{
	int Nt=1,p[N+5],q[N+5];struct node {int F,S[30];}O[N+5];
	I void Ins(CI id,char* s,CI l) {RI x=1;//插入串
		for(RI i=1,t;i<=l;++i) !O[x].S[t=s[i]&31]&&(O[x].S[t]=++Nt),x=O[x].S[t];p[id]=x;}
	I void Build()//建AC自动机
	{
		RI i,k,H=1,T=0;for(i=1;i<=26;++i) (O[1].S[i]?O[q[++T]=O[1].S[i]].F:O[1].S[i])=1;
		W(H<=T) for(k=q[H++],i=1;i<=26;++i) (O[k].S[i]?O[q[++T]=O[k].S[i]].F:O[k].S[i])=O[O[k].F].S[i];
		for(i=2;i<=Nt;++i) T::Add(O[i].F,i);T::Init(1);//建fail树预处理
	}
	I void U(char* s,CI l) {for(RI i=1,x=1;i<=l;++i) x=O[x].S[s[i]&31],A.U(T::dI[x]);}//给所有前缀打标记
	I int Q(CI id) {return A.Q(T::dO[p[id]])-A.Q(T::dI[p[id]]-1);}//询问一个串对应节点子树内标记和
}
int main()
{
	RI Qt,i,x,y,z;scanf("%d%d",&n,&Qt);
	for(i=1;i<=n;++i) scanf("%s",s+(bg[i]=bg[i-1]+l[i-1])+1),AC::Ins(i,s+bg[i],l[i]=strlen(s+bg[i]+1));
	for(AC::Build(),i=1;i<=Qt;++i) scanf("%d%d%d",&x,&y,&z),q[x-1].push_back(Q(i,z,-1)),q[y].push_back(Q(i,z,1));//把询问离线
	for(i=1;i<=n;++i) for(AC::U(s+bg[i],l[i]),it=q[i].begin();it!=q[i].end();++it) ans[it->p]+=it->op*AC::Q(it->k);//不断加串做全局询问
	for(i=1;i<=Qt;++i) printf("%d\n",ans[i]);return 0;
}

标签:AC,int,CF547E,CI,lnk,自动机,Friends,define
来源: https://www.cnblogs.com/chenxiaoran666/p/CF547E.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有