【题目】
CF
给定 n n n个串 a i a_i ai​, Q Q Q组询问 a l ∼ a r a_l\sim a_r al​∼ar​中 a x a_{x} ax​出现了多少次。
∑ ∣ a i ∣ ≤ 2 × 1 0 5 , Q ≤ 5 × 1 0 5 \sum |a_i| \leq 2\times 10^5,Q\leq 5\times 10^5 ∑∣ai​∣≤2×105,Q≤5×105

【解题思路】
这种东西显然 SAM \text{SAM} SAM线段树合并就可以做了吧。建出 SAM \text{SAM} SAM,那么询问就是子树中编号为某段区间 e n d p o s endpos endpos的数量。我们以所在串编号建立线段树,线段树合并 r i g h t right right集合即可。

然而我又忘记SAM是节点数是两倍了

什么?不会 SAM \text{SAM} SAM?没有关系。

其实做这题的初衷就是练 AC \text{AC} AC自动机的,原理一样,但显然不够优秀。

我们建出 AC \text{AC} AC自动机以后,再用每个串跑一次,对于当前串 s s s前缀 s i s_i si​,在其对应 f a i l fail fail树的位置贡献 + 1 +1 +1,那么这个前缀的所有后缀都会被贡献一次,即它 f a i l fail fail树子树中所有节点对于 s s s这个串的计数都会 + 1 +1 +1。

现在问题就是统计子树内代表串编号在 [ l , r ] [l,r] [l,r]的所有节点权值和。
观察到询问编号在 [ l , r ] [l,r] [l,r]这个东西是可以差分的,那么我们线段树的下标显然就不是编号了。一种显然的方式是,我们对 f a i l fail fail树求出它的 d f s dfs dfs序,然后以此为下标建立线段树,可持久化一下就行了。

我们也可以使用离线 B I T BIT BIT的方式解决这个问题,可以获得更优秀的空间复杂度。

两种做法时间复杂度是一样的,都是 O ( ( n + q ) log ⁡ n ) O((n+q)\log n) O((n+q)logn)。

两种做法都写了一次,明显感觉到了 SAM \text{SAM} SAM在处理某些问题时的优越性以及直观(在入门了以后)。

【参考代码】
SAM + \text{SAM}+ SAM+线段树合并

#include<bits/stdc++.h>
using namespace std;const int N=4e5+10,M=N*40;
int n,Q;
char s[N];namespace IO
{int read(){int ret=0;char c=getchar();while(!isdigit(c)) c=getchar();while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return ret;}void write(int x){if(x>9)write(x/10);putchar(x%10^48);}void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;namespace Tree
{int rt[N];struct Segment{int sz,ls[M],rs[M],sum[M];void update(int &x,int l,int r,int p){if(!x) x=++sz; ++sum[x];if(l==r) return;int mid=(l+r)>>1;if(p<=mid) update(ls[x],l,mid,p);else update(rs[x],mid+1,r,p);}int query(int x,int l,int r,int L,int R){if(!x) return 0;if(L<=l && r<=R) return sum[x];int mid=(l+r)>>1,res=0;if(L<=mid) res+=query(ls[x],l,mid,L,R);if(R>mid) res+=query(rs[x],mid+1,r,L,R);return res;}int merge(int x,int y){if(!x || !y) return x+y;int z=++sz;ls[z]=merge(ls[x],ls[y]);rs[z]=merge(rs[x],rs[y]);sum[z]=sum[x]+sum[y];return z;}}tr;
}
using namespace Tree;namespace SAM
{int p[N],b[N],c[N],id[N];struct SAM{int sz,las,fa[N],mx[N],ch[N][26];void extend(int x){int p,q,np,nq;if(ch[las][x]){p=las;q=ch[p][x];if(mx[q]==mx[p]+1) {las=q;return;}nq=++sz;mx[nq]=mx[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[q]));fa[nq]=fa[q];fa[q]=nq;for(;p && ch[p][x]==q;p=fa[p]) ch[p][x]=nq;las=nq;return;}p=las;np=las=++sz;mx[np]=mx[p]+1;for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;if(!p) fa[np]=1;else{q=ch[p][x];if(mx[q]==mx[p]+1) fa[np]=q;else {nq=++sz;mx[nq]=mx[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[q]));fa[nq]=fa[q];fa[np]=fa[q]=nq;for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;}}}void merge(){for(int i=1;i<=sz;++i) b[mx[i]]++;for(int i=1;i<=sz;++i) b[i]+=b[i-1];for(int i=sz;i;--i) c[b[mx[i]]--]=i;for(int i=sz,x;i>1;--i) x=c[i],rt[fa[x]]=tr.merge(rt[x],rt[fa[x]]); }}S;
}
using namespace SAM;int main()
{#ifndef ONLINE_JUDGEfreopen("CF547E.in","r",stdin);freopen("CF547E.out","w",stdout);
#endifn=read();Q=read();S.sz=S.las=1;for(int i=1,l;i<=n;++i) {scanf("%s",s+1);l=strlen(s+1);S.las=1;for(int j=1;j<=l;++j) S.extend(s[j]-'a'),tr.update(rt[S.las],1,n,i);p[i]=S.las;}S.merge();while(Q--){int l=read(),r=read(),id=read();writeln(tr.query(rt[p[id]],1,n,l,r));}return 0;
}

AC \text{AC} AC自动机+主席树

#include<bits/stdc++.h>
#define pb push_back
using namespace std;const int N=2e5+10,M=N*40;
int n,Q;namespace IO
{int read(){int ret=0;char c=getchar();while(!isdigit(c)) c=getchar();while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return ret;}void write(int x){if(x>9)write(x/10);putchar(x%10^48);}void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;namespace Tree
{vector<int>e[N];int ind,rt[N],st[N],en[N];struct Segment{int sz,ls[M],rs[M],sum[M];void copy(int x,int y){ls[x]=ls[y];rs[x]=rs[y];sum[x]=sum[y];}void pushup(int x){sum[x]=sum[ls[x]]+sum[rs[x]];}void update(int y,int &x,int l,int r,int p){//printf("%d %d %d %d %d\n",y,x,l,r,p);x=++sz;copy(x,y);if(l==r){sum[x]++;return;}int mid=(l+r)>>1;if(p<=mid) update(ls[y],ls[x],l,mid,p);else update(rs[y],rs[x],mid+1,r,p);pushup(x);//printf("%d %d %d\n",l,r,sum[x]);}int query(int y,int x,int l,int r,int L,int R){if(L<=l && r<=R) return sum[x]-sum[y];int mid=(l+r)>>1,res=0;if(L<=mid) res+=query(ls[y],ls[x],l,mid,L,R);if(R>mid) res+=query(rs[y],rs[x],mid+1,r,L,R);return res;}}tr;void dfs(int x){st[x]=++ind;for(int i=0;i<(int)e[x].size();++i) dfs(e[x][i]);en[x]=ind;}
}
using namespace Tree;namespace ACM
{int ed[N],len[N];char s[N];vector<int>ts[N];queue<int>q;struct ACM{int sz,rt,fail[N],ch[N][26];void init(){rt=sz=1;}void in(int id){scanf("%s",s+1);len[id]=strlen(s+1);int now=rt;//printf("%d %d\n",id,len[id]);for(int i=1;i<=len[id];++i){ts[id].pb(s[i]-'a');int x=s[i]-'a';if(!ch[now][x]) ch[now][x]=++sz;now=ch[now][x];}ed[id]=now;}void getfail(){q.push(rt);while(!q.empty()){int x=q.front();q.pop();for(int i=0;i<26;++i){if(!ch[x][i]) continue;int t=fail[x],t1=ch[x][i];while(t && !ch[t][i]) t=fail[t];fail[t1]=t?ch[t][i]:rt;q.push(t1);}}for(int i=1;i<=sz;++i) e[fail[i]].pb(i);}}S;
}
using namespace ACM;void build(int y,int &x,int id)
{int now=S.rt,las=y;//printf("%d %d %d %d\n",id,len[id],st[ed[id]],en[ed[id]]);for(int i=0;i<len[id];++i){now=S.ch[now][ts[id][i]];tr.update(las,x,1,ind,st[now]);las=x;}
}int main()
{#ifndef ONLINE_JUDGEfreopen("CF547E.in","r",stdin);freopen("CF547E.out","w",stdout);
#endifn=read();Q=read();S.init();for(int i=1;i<=n;++i) S.in(i);S.getfail();dfs(S.rt);for(int i=1;i<=n;++i) build(rt[i-1],rt[i],i);while(Q--){int l=read(),r=read(),id=read();printf("%d\n",tr.query(rt[l-1],rt[r],1,ind,st[ed[id]],en[ed[id]]));}return 0;
}

【SAM套路/AC自动机+主席树】CF547E Mike and Friends相关推荐

  1. BZOJ 2754 [SCOI2012]喵星球上的点名 (AC自动机、树状数组)

    吐槽: 为啥很多人用AC自动机暴力跳都过了?复杂度真的对么? 做法一: AC自动机+树状数组 姓名的问题,中间加个特殊字符连起来即可. 肯定是对点名串建AC自动机(map存儿子),然后第一问就相当于问 ...

  2. LOJ 3055 「HNOI2019」JOJO—— kmp自动机+主席树

    题目:https://loj.ac/problem/3055 先写了暴力.本来想的是 n<=300 的那个在树上暴力维护好整个字符串, x=1 的那个用主席树维护好字符串和 nxt 数组.但 x ...

  3. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 2545  Solved: 1419 [Submit][S ...

  4. CodeForces - 1437G Death DBMS(AC自动机fail树上树链剖分建线段树/暴跳fail)

    题目链接:点击查看 题目大意:给出 n 个模式串,每个模式串初始时的权值为 0,然后有 m 次操作: 1 i x:将第 i 个模式串的权值修改为 x 2 s:给出一个字符串 s,询问字符串 s 作为主 ...

  5. 洛谷P5357 - 【模板】AC自动机(二次加强版)(AC自动机+fail树)

    题目链接:点击查看 题目大意:给出n个模式串,问在主串中分别出现了多少次 题目分析:如果像以往那样,在匹配的时候fail指针乱跳的话,那么是错误的AC自动机使用方法,时间复杂度也大大上升,接近于暴力的 ...

  6. P7456 [CERC2018] The ABCD Murderer (ac自动机+线段树优化dp/反向st)

    做法 ac自动机预处理出最长后缀 f[i]=max(f[i-1],f[i-2],-,f[i-最长后缀])+1; 转移即可 下面有一种反向st维护的学到了 #include <bits/stdc+ ...

  7. 1285. 单词 ac自动机 + fail树

    传送门 文章目录 题意: 思路: 题意: 一篇论文由若干单词构成,且单词间是隔开的,给你nnn个单词,要求你计算每个单词在论文中出现了多少次. 1≤n≤2001\le n\le 2001≤n≤200, ...

  8. BZOJ 2434 Luogu P2414 [NOI2011]阿狸的打字机 (AC自动机、树状数组)

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2434 题解: 我写的是离线做法,不知道有没有在线做法. 转化一波题意,\(x\)在AC ...

  9. bzoj 2434 [Noi2011]阿狸的打字机(AC自动机+fail树+dfs序+树状数组)

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 3521  Solved: 1913 [Submit][S ...

最新文章

  1. 将 SqlDataReader 类用于快速只进数据游标
  2. React绑定this的三种方式
  3. 如何评估模型的预测性能?
  4. Linux中的update和upgrade的作用
  5. java输入args不能为空_java程序入口为什么有的空括号在args前面有的?
  6. Windows RDP远程桌面无密码账户
  7. kafka是什么_Kafka为什么快到根本停不下来?
  8. Fiddler远程抓包
  9. PyQt实现读取文件内容并绘制曲线
  10. 如何利用新浪微博开放平台API获取新浪微博用户数据
  11. 计算机的工作原理(冯诺依曼体系)
  12. 浏览器 播放音频(IE,谷歌)
  13. C#字符串解析成16进制,并计算校验和
  14. warmup与余弦退火学习率
  15. 【Lucene】挖掘相关搜索词
  16. Accessing non existent property lineno of module
  17. 中小企业如何才能招聘到合适的程序员?
  18. 机器学习SVM--基于手写字体识别
  19. win10系统开启IIS服务
  20. 一个可以扩容C盘的第三方免费软件

热门文章

  1. 2008 r2 server 提权_某次Windows渗透提权过程
  2. SQL 计算累积销售额,百分比
  3. 投放共享单车需要什么批文_共享单车须经审批方可投放
  4. bongocat猫咪键盘怎么用?
  5. [ 图像分类 ] 经典网络模型5——DenseNet 详解与复现
  6. 2017微信公开课张小龙小程序演讲视频
  7. 【思维导图】数据库知识框架
  8. EMC电磁兼容知识框架
  9. 嘀嗒出行显示服务器开小差,嘀嗒出行闪退怎么办 解决方法
  10. linux下Android开机动画制作