Codeforces 547E - Mike and Friends(AC 自动机+树状数组)
好久每做过 AC 自动机的题了……做几个题回忆一下罢
AC 自动机能够解决多串匹配问题,注意是匹配,碰到前后缀的问题那多半不在 AC 自动机能解决的范围内。
在初学 AC 自动机的时候相信大家都做过一道题叫做 P2414 [NOI2011] 阿狸的打字机。在这道题中我们用到了两棵树,一棵就是所有串的字典树,称为 trie 树,令一棵是求出每个点的 \(fail_i\) 后,对于所有不是根节点的 \(i\) 连边 \((fail_i,i)\) 后形成的树,称为 fail 树。
在那道题中我们学到了一个很重要的结论,那就是一个字符串 \(s\) 在另一个字符串 \(t\) 中出现的次数,等于 \(t\) 的结尾位置在 trie 树上的祖先中,有多少个在 \(s\) 的结尾位置在 fail 树的子树中。可以简单记为“模式串 fail 树向下,文本串 trie 树向上”。这个结论是解不少 AC 自动机与数据结构结合的题的基础。
那么我们就用这个结论来解这道题。首先建出 AC 自动机。我们考虑可以用差分的思想把一个询问拆成 \((l-1,k)\) 和 \((r,k)\) 两部分,二者相减得到答案。于是现在问题转化为处理形如“\(s_y\) 在前 \(x\) 个字符串中出现了多少次”,我们把这样的询问都挂到 \(x\) 上并动态地添加字符串。当我们加入一个字符串 \(s_i\) 的时候,假设 \(s_i\) 的结尾位置为 \(x\),由于 \(s_i\) 是文本串要在 trie 树上向上跳,我们就枚举 \(x\) 的所有祖先 \(y\) 并在 \(y\) 位置上加 \(1\),表示 \(y\) 节点的访问次数多了 $1$1。查询的时候就求出 \(s_k\) 的结尾位置在 fail 树中的子树中有多少个 \(1\),这个显然可以树状数组+DFS 序搞定。算下时间复杂度,显然对于一个字符串 \(i\),它在 trie 树上的祖先个数就是 \(|s_i|\),故我们最多在树状数组上加 \(\sum|s_i|\) 次,复杂度线对。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=2e5;
const int MAXQ=5e5;
const int ALPHA=26;
int n,qu;string s[MAXN+5];
int ch[MAXN+5][ALPHA+2],fail[MAXN+5],ncnt=0,ed[MAXN+5],ans[MAXQ+5];
void insert(string s,int id){
int cur=0;
for(int i=0;i<s.size();i++){
if(!ch[cur][s[i]-'a']) ch[cur][s[i]-'a']=++ncnt;
cur=ch[cur][s[i]-'a'];
} ed[id]=cur;
}
void getfail(){
queue<int> q;
for(int i=0;i<ALPHA;i++) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<ALPHA;i++){
if(ch[x][i]){fail[ch[x][i]]=ch[fail[x]][i];q.push(ch[x][i]);}
else ch[x][i]=ch[fail[x]][i];
}
}
}
int hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int tim=0,bgt[MAXN+5],edt[MAXN+5];
void dfs(int x){bgt[x]=++tim;for(int e=hd[x];e;e=nxt[e]) dfs(to[e]);edt[x]=tim;}
int t[MAXN+5];
void add(int x,int v){for(int i=x;i<=(ncnt+1);i+=(i&(-i))) t[i]+=v;}
int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;}
vector<pair<pii,int> > qv[MAXN+5];
int main(){
scanf("%d%d",&n,&qu);for(int i=1;i<=n;i++) cin>>s[i],insert(s[i],i);
getfail();for(int i=1;i<=ncnt;i++) adde(fail[i],i);dfs(0);
for(int i=1;i<=qu;i++){
int l,r,k;scanf("%d%d%d",&l,&r,&k);
qv[r].pb(mp(mp(i,1),k));qv[l-1].pb(mp(mp(i,-1),k));
}
for(int i=1;i<=n;i++){
int cur=0;
for(int j=0;j<s[i].size();j++){
cur=ch[cur][s[i][j]-'a'];add(bgt[cur],1);
}
ffe(it,qv[i]){
int x=it->se,id=it->fi.fi,mul=it->fi.se;
ans[id]+=mul*(query(edt[ed[x]])-query(bgt[ed[x]]-1));
}
}
for(int i=1;i<=qu;i++) printf("%d\n",ans[i]);
return 0;
}
Codeforces 547E - Mike and Friends(AC 自动机+树状数组)的更多相关文章
- 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树
正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...
- 【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序
[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写 ...
- CodeForces 547E Mike and Friends AC自动机 主席树
题意: 给出\(n\)个字符串\(s_i\)和\(q\)个询问: \(l,r,k\):\(\sum\limits_{i=l}^{r}count(i, k)\),其中\(count(i,j)\)表示\( ...
- Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)
题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...
- BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 4140 Solved: 2276[Submit][Status][Discuss] Descript ...
- BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并
题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- HDU 6096 String(AC自动机+树状数组)
题意 给定 \(n\) 个单词,\(q\) 个询问,每个询问包含两个串 \(s_1,s_2\),询问有多少个单词以 \(s_1\) 为前缀, \(s_2\) 为后缀,前后缀不能重叠. \(1 \leq ...
- bzoj 2434 AC自动机+树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 3493 Solved: 1909[Submit][Sta ...
- [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组
[NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...
随机推荐
- 【UE4】虚幻引擎技术直播汇总(含中英文直播)
B站虚幻引擎官方账号 中文直播 [中文直播]第35期 | 使用GIS在UE中创造真实地球风貌 | Epic 周澄清 [中文直播]第34期 | 包教包会的Epic MegaGrants申请之道 | Ep ...
- 第五章第四周习题: Transformers Architecture with TensorFlow
目录 Transformer Network Packages 1 - Positional Encoding 1.1 - Sine and Cosine Angles Exercise 1 - ge ...
- MySQL:补充知识
MySQL补充知识 在学习完 MySQL 基础与提高内容后: 基础知识笔记: MySQL:基础语法-1 MySQL:基础语法-2 MySQL:基础语法-3 MySQL:基础语法-4 提高知识笔记: M ...
- [对对子队]会议记录4.20(Scrum Meeting11)
今天已完成的工作 何瑞 工作内容:搭建第三关,添加了运行指令标识 相关issue:搭建关卡2.3 相关签入:4.20签入1 4.20签入2 吴昭邦 工作内容:搭建第三关 相关iss ...
- elasticsearch的bulk(批量)操作
在es中我们可能会有这么一种需求,即有时需要批量向es中插入或更新或删除数据,如果一条一条数据的操作,那么速度必然很慢,那么es的bulk api就可以派上用场. delete 删除操作,只需要写一个 ...
- eureka服务端的高可用
eureka client的高可用这个很简单,只需要向eureka服务端上多注册几个实例即可,那么eureka server端如何实现高可用呢?其实eureka server 端也是可以做为一个客户端 ...
- 请问为什么要用三极管驱动mos,直接用mos有什么缺点呢?
可能无法完全导通,电流可能过小使导通所需时间变长,最终导致发热严重 回复 举报 csaaa DIY七级 3# 发表于 2016-7-12 14:11:59 直接驱动mos也没什么问 ...
- 彻底搞通TCP滑动窗口
在我们当初学习网络编程的时候,都接触过TCP,在TCP中,对于数据传输有各种策略,比如滑动窗口.拥塞窗口机制,又比如慢启动.快速恢复.拥塞避免等.通过本文,我们将了解滑动窗口在TCP中是如何使用的. ...
- SPOJ GSS8 - Can you answer these queries VIII | 平衡树
题目链接 这一道题的修改操作用平衡树都很容易实现,难处理的是询问操作. 要想解决询问操作,只要知道如何在平衡树上快速合并左右两个区间的答案即可. 设$Ans_{[l,r]}^k=\sum\limits ...
- SpringCloud微服务实战——搭建企业级开发框架(十四):集成Sentinel高可用流量管理框架【限流】
Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流.流量整形.熔断降级.系统负载保护.热点防护等多个维度来帮助开发者保障微服务的稳定性. Sentinel 具有 ...