题面传送门

好久每做过 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 自动机+树状数组)的更多相关文章

  1. 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树

    正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...

  2. 【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

    [题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写 ...

  3. CodeForces 547E Mike and Friends AC自动机 主席树

    题意: 给出\(n\)个字符串\(s_i\)和\(q\)个询问: \(l,r,k\):\(\sum\limits_{i=l}^{r}count(i, k)\),其中\(count(i,j)\)表示\( ...

  4. Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)

    题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...

  5. BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)

    Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4140  Solved: 2276[Submit][Status][Discuss] Descript ...

  6. BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  7. HDU 6096 String(AC自动机+树状数组)

    题意 给定 \(n\) 个单词,\(q\) 个询问,每个询问包含两个串 \(s_1,s_2\),询问有多少个单词以 \(s_1\) 为前缀, \(s_2\) 为后缀,前后缀不能重叠. \(1 \leq ...

  8. bzoj 2434 AC自动机+树状数组

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 3493  Solved: 1909[Submit][Sta ...

  9. [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组

    [NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...

随机推荐

  1. Noip模拟31 2021.8.5

    T1 Game 当时先胡了一发$\textit{Next Permutation}$... 然后想正解,只想到贪心能求最大得分,然后就不会了.. 然后就甩个二十分的走了... 正解的最大得分(叫它$k ...

  2. VS2017+QT5.12.10+QGIS3.16环境搭建及开发全流程

    题记:大力发展生产力,助力高效采集.(转载请注明出处https://www.cnblogs.com/1024bytes/p/15477374.html) 本篇随笔分为五个部分: 一.获取QGIS3.1 ...

  3. iPhone SE切换颜色特效

    Apple 网站的特效, iPhone SE 共有黑.白.红三种颜色,在卷动页面的时候会逐步替换,看起来效果非常时尚,在此供上代码学习. <!DOCTYPE html> <html& ...

  4. linux cut

    参考:Linux cut 命令详解_Linux_脚本之家 (jb51.net) 参考:cut命令_Linux cut 命令用法详解:连接文件并打印到标准输出设备上 (linuxde.net)

  5. 数字设计中的时钟与约束(gate)

    转载:https://www.cnblogs.com/IClearner/p/6440488.html 最近做完了synopsys的DC workshop,涉及到时钟的建模/约束,这里就来聊聊数字中的 ...

  6. Python import Queue ImportError: No module named 'Queue'

    python3 中引入Queue 会报出这个问题 python3 中这样引入 import queue python2 中这样引入 import Queue 为了兼容 可以这样 import sys ...

  7. kail入侵xp实例

    Kali的IP地址是192.168.0.112 Windows XP的IP地址是192.168.0.108 本文演示怎么使用Metasploit入侵windows xp sp3. 启动msfconso ...

  8. 【mysql2】下载安装mysql5.7版|不再更新系列

    一.下载MySQL 5.7 版 MySQL 5.7 版:官网下载地址 https://dev.mysql.com/downloads/windows/installer/5.7.html 下载的是50 ...

  9. k8s入坑之路(7)kubernetes设计精髓List/Watch机制和Informer模块详解

    1.list-watch是什么 List-watch 是 K8S 统一的异步消息处理机制,保证了消息的实时性,可靠性,顺序性,性能等等,为声明式风格的API 奠定了良好的基础,它是优雅的通信方式,是 ...

  10. 谷粒 | 18 | Hystrix熔断器

    Spring Cloud调用接口过程 Spring Cloud 在接口调用上,大致会经过如下几个组件配合: Feign ----->Hystrix ->Ribbon ->Http C ...