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


    点此看题面

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

    (AC)自动机+树状数组

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

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

    考虑把询问离线,变成(s_{1sim r})(s_k)的出现次数减去(s_{1sim 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
    ",ans[i]);return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    CSS3边框与圆角
    测试
    linux 日常高逼格命令统计
    python 获取目录视频时长,大小
    记一次 irqbalance: WARNING, didn't collect load info for all cpus, balancing is broken问题
    loging模块
    修改CentOS7系列网卡名称为传统名称eth0格式
    ELK测试
    linux安装python3-pip3-django的过程
    17.基于scrapy-redis两种形式的分布式爬虫
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF547E.html
Copyright © 2020-2023  润新知