题意

给你一些串,还有一些询问

问你第x个串在第y个串中出现了多少次

思路

对这些串建ac自动机

根据fail树的性质:若x节点是trie中root到t任意一个节点的fail树的祖先,那么x一定是y的子串

而x在y中出现的次数为以x为fail树中的根节点的子树中,有多少个节点是trie树中根节点到y的

首先对询问离线

由于这题是一个节点一个节点建的ac自动机,所以我们可以根据这个建立的路径来维护一个当前节点到根的路径

路径上的点权为1,其余为0

每次到达一个询问到的y,我们就查询此时x的fail树中的子树和,这个可以预处理出fail树的dfs序用树状数组维护

但是如果排除这题的特殊性,要通过遍历trie的方式来更新答案

因为在操作fail的过程中,trie改变了

else trie[now][i]=trie[fail[now]][i];

也就是说bfs构造fail指针的时候,有时候会让trie指向之前的节点,也就是(trie中)高度比较小的节点

所以dfs只需要这样写

void dfs(int x){
//printf("==%d\n",x);
add(bg[x],1);
for(int i = 0; i < (int)Q[x].size(); i++){
int y = Q[x][i].fst;
ans[Q[x][i].sc]=sum(ed[y])-sum(bg[y]-1);
}
for(int i = 0; i < 26; i++){
int y = ac.trie[x][i];
if(!y||h[y]<=h[x])continue;
dfs(y);
}
add(bg[x],-1);
}

代码

版本一

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<deque>
#include<set>
#include<vector>
#include<map>
#include<functional> #define fst first
#define sc second
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lc root<<1
#define rc root<<1|1 using namespace std; typedef double db;
typedef long double ldb;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PI;
typedef pair<ll,ll> PLL; const db eps = 1e-6;
const int mod = 1e9+7;
const int maxn = 3e5+100;
const int maxm = 2e6+100;
const int inf = 0x3f3f3f3f;
const db pi = acos(-1.0); vector<int>v[maxn];
int id[maxn];
struct AC{
//局部变量没有默认0!
int trie[maxn][26];
int num[maxn];//单词出现次数
int fail[maxn],fa[maxn];
int vis[maxn];//ask函数用到
int tot,rt;
int ntot;
//多测可写个init
void init(){tot=0;mem(vis,0);mem(trie,0);rt=0;ntot=0;}
void add(char c){
if(c=='P'){
num[rt]++;
id[++ntot]=rt;
return;
}
if(c=='B'){
rt=fa[rt];
return;
}
int x = c-'a';
if(!trie[rt][x]){
trie[rt][x]=++tot;
fa[tot]=rt;
}
//fa[trie[rt][x]]=rt;
rt=trie[rt][x];
}
void build(){
queue<int>q;
for(int i = 0; i < 26; i++){
if(trie[0][i]){
//fail[trie[0][i]]=0;
//v[0].pb(trie[0][i]);
q.push(trie[0][i]);
}
}
while(!q.empty()){
int now = q.front();
q.pop();
for(int i = 0; i < 26; i++){
////让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个相同节点)
if(trie[now][i]){
fail[trie[now][i]]=trie[fail[now]][i];
//v[trie[fail[now]][i]].pb(trie[now][i]);
q.push(trie[now][i]);
}
else trie[now][i]=trie[fail[now]][i];
//否则就让当前节点的这个子节点指向当前节点fail指针的这个子节点
}
v[fail[now]].pb(now);
}
} }ac;
char a[maxn];
int bg[maxn],ed[maxn];
int S[maxn],tot;
void gao(int x){
S[++tot]=x;
bg[x]=tot;
for(int i = 0; i < (int)v[x].size(); i++){
int y =v[x][i];
gao(y);
}
//S[++tot]=x;
ed[x]=tot;
}
int tree[maxn];
int lowbit(int x){return x&-x;}
void add(int x, int c){
for(int i = x; i <= tot; i+=lowbit(i)){tree[i]+=c;}
}
int sum(int x){
int ans =0;
for(int i = x; i; i-=lowbit(i))ans+=tree[i];
return ans;
}
vector<PI>Q[maxn];
int ans[maxn];
/*
void dfs(int x){
//printf("%d\n",x);
add(bg[x],1);
for(int i = 0; i < (int)Q[x].size(); i++){
int y = Q[x][i].fst;
ans[Q[x][i].sc]=sum(ed[y])-sum(bg[y]-1);
}
for(int i = 0; i < 26; i++){
int y = ac.trie[x][i];
if(!y||(y==ac.trie[ac.fail[x]][i]))continue;
dfs(y);
}
add(bg[x],-1);
}*/
void sv(){
int now = 0;
int n = strlen(a+1);
int world=0;
for(int i = 1; i <= n; i++){
if(a[i]=='B'){
add(bg[now],-1);
now=ac.fa[now];
continue;
}
else if(a[i]=='P'){
world++;
for(int j = 0; j < (int)Q[now].size(); j++){
int x = Q[now][j].fst;
int y = Q[now][j]. sc;
ans[y]=sum(ed[x])-sum(bg[x]-1);
}
continue;
}
else{
int x=a[i]-'a';
now=ac.trie[now][x];
add(bg[now],1);
}
}
}
int main(){
ac.init();
tot=0;
scanf("%s", a+1);
int n = strlen(a+1);
for(int i = 1; i <= n; i++){
ac.add(a[i]);
}
ac.build();
int m;
scanf("%d", &m);
for(int i = 1; i <= m; i++){
int x,y;
scanf("%d %d", &x, &y);
x=id[x];y=id[y];
Q[y].pb(make_pair(x,i));
}
gao(0);
//dfs(0);
sv();
for(int i = 1; i <= m; i++){
printf("%d\n",ans[i]);
}
return 0;
}
/*
aPaPBbP
3
1 2
1 3
2 3 a
aa
aa
*/

版本二

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<deque>
#include<set>
#include<vector>
#include<map>
#include<functional> #define fst first
#define sc second
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lc root<<1
#define rc root<<1|1 using namespace std; typedef double db;
typedef long double ldb;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PI;
typedef pair<ll,ll> PLL; const db eps = 1e-6;
const int mod = 1e9+7;
const int maxn = 3e5+100;
const int maxm = 2e6+100;
const int inf = 0x3f3f3f3f;
const db pi = acos(-1.0); vector<int>v[maxn];
int id[maxn];
int h[maxn];
struct AC{
//局部变量没有默认0!
int trie[maxn][26];
int num[maxn];//单词出现次数
int fail[maxn],fa[maxn];
int vis[maxn];//ask函数用到
int tot,rt;
int ntot;
int H;
//多测可写个init
void init(){tot=0;mem(vis,0);mem(trie,0);rt=0;ntot=0;H=0;}
void add(char c){
if(c=='P'){
num[rt]++;
id[++ntot]=rt;
return;
}
if(c=='B'){
rt=fa[rt];H--;
return;
}
int x = c-'a';
if(!trie[rt][x]){
trie[rt][x]=++tot;
fa[tot]=rt;
}
//fa[trie[rt][x]]=rt;
rt=trie[rt][x];
h[rt]=++H;
}
void build(){
queue<int>q;
for(int i = 0; i < 26; i++){
if(trie[0][i]){
fail[trie[0][i]]=0;
//v[0].pb(trie[0][i]);
q.push(trie[0][i]);
}
}
while(!q.empty()){
int now = q.front();
q.pop();
for(int i = 0; i < 26; i++){
////让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个相同节点)
if(trie[now][i]){
fail[trie[now][i]]=trie[fail[now]][i];
//v[trie[fail[now]][i]].pb(trie[now][i]);
q.push(trie[now][i]);
}
else trie[now][i]=trie[fail[now]][i];
//否则就让当前节点的这个子节点指向当前节点fail指针的这个子节点
}
v[fail[now]].pb(now);
}
} }ac;
char a[maxn];
int bg[maxn],ed[maxn];
int S[maxn],tot;
void gao(int x){
S[++tot]=x;
bg[x]=tot;
for(int i = 0; i < (int)v[x].size(); i++){
int y =v[x][i];
gao(y);
}
//S[++tot]=x;
ed[x]=tot;
}
int tree[maxn];
int lowbit(int x){return x&-x;}
void add(int x, int c){
for(int i = x; i <= tot; i+=lowbit(i)){tree[i]+=c;}
}
int sum(int x){
int ans =0;
for(int i = x; i; i-=lowbit(i))ans+=tree[i];
return ans;
}
vector<PI>Q[maxn];
int ans[maxn]; void dfs(int x){
//printf("==%d\n",x);
add(bg[x],1);
for(int i = 0; i < (int)Q[x].size(); i++){
int y = Q[x][i].fst;
ans[Q[x][i].sc]=sum(ed[y])-sum(bg[y]-1);
}
for(int i = 0; i < 26; i++){
int y = ac.trie[x][i];
if(!y||h[y]<=h[x])continue;
dfs(y);
}
add(bg[x],-1);
}
void sv(){
int now = 0;
int n = strlen(a+1);
int world=0;
for(int i = 1; i <= n; i++){
if(a[i]=='B'){
add(bg[now],-1);
now=ac.fa[now];
continue;
}
else if(a[i]=='P'){
world++;
for(int j = 0; j < (int)Q[now].size(); j++){
int x = Q[now][j].fst;
int y = Q[now][j]. sc;
ans[y]=sum(ed[x])-sum(bg[x]-1);
}
continue;
}
else{
int x=a[i]-'a';
now=ac.trie[now][x];
add(bg[now],1);
}
}
}
int main(){
ac.init();
tot=0;
scanf("%s", a+1);
int n = strlen(a+1);
for(int i = 1; i <= n; i++){
ac.add(a[i]);
}
ac.build();
int m;
scanf("%d", &m);
for(int i = 1; i <= m; i++){
int x,y;
scanf("%d %d", &x, &y);
x=id[x];y=id[y];
Q[y].pb(make_pair(x,i));
}
gao(0);
dfs(0);
//sv();
for(int i = 1; i <= m; i++){
printf("%d\n",ans[i]);
}
return 0;
}
/*
asPdPasPddPhBdPhPnaPasP
8
1 5
2 6
3 8
4 3
7 7
2 5
6 8
1 8 a
aa
aa
*/

BZOJ 2434 阿狸的打字机(ac自动机+dfs序+树状数组)的更多相关文章

  1. BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

    一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...

  2. NOI 2011 阿狸的打字机 (AC自动机+dfs序+树状数组)

    题目大意:略(太长了不好描述) 良心LOJ传送门 先对所有被打印的字符串建一颗Trie树 观察数据范围,并不能每次打印都从头到尾暴力建树,而是每遍历到一个字符就在Trie上插入这个字符,然后记录每次打 ...

  3. BZOJ2434[Noi2011]阿狸的打字机——AC自动机+dfs序+树状数组

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

  4. 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

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

  5. BZOJ_2434_[NOI2011]_阿狸的打字机_(AC自动机+dfs序+树状数组)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2434 给出\(n\)个字符串,\(m\)个询问,对于第\(i\)个询问,求第\(x_i\)个字 ...

  6. BZOJ2434: [NOI2011]阿狸的打字机(AC自动机+dfs序+树状数组)

    [NOI2011]阿狸的打字机 题目链接:https://www.luogu.org/problemnew/show/P2414 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. ...

  7. CodeForces -163E :e-Government (AC自动机+DFS序+树状数组)

    The best programmers of Embezzland compete to develop a part of the project called "e-Governmen ...

  8. BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组

    BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组 Description Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是 ...

  9. [NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]

    题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 ...

随机推荐

  1. 我们为什么会删除不了集群的 Namespace?

    作者 | 声东  阿里云售后技术专家 导读:阿里云售后技术团队的同学,每天都在处理各式各样千奇百怪的线上问题.常见的有网络连接失败.服务器宕机.性能不达标及请求响应慢等.但如果要评选的话,什么问题看起 ...

  2. matlab读取excel文件中的数据

    1.读取sheet1中的所有数据 1.1首先我们建立一个sheet表,表名为‘111’ 1.2 默认这些文本以及数字都放在sheet1中,我们将此excel选入当前工作目录(必要步骤), 选入当前工作 ...

  3. React-router路由4.0版本用法

    第一步:引包两个 命令:cnpm i -S react-router react-router-dom 第二步:用路由管理APP页面 1.创建主路由管理页面,在这里使用了路由嵌套 import Rea ...

  4. 你知道Java中的CopyOnWriteArrayList吗?

    CopyOnWrite CopyOnWrite是什么? CopyOnWriteArrayList源码分享? CopyOnWriteArrayList使用场景? CopyOnWriteArrayList ...

  5. P1850 换教室 期望dp

    P1850 换教室 题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 2n2n 节课程安排在 nn 个时间段上.在第 ii(1 \leq ...

  6. cogs 176. [USACO Feb07] 奶牛聚会 dijkstra

    176. [USACO Feb07] 奶牛聚会 ★☆   输入文件:sparty.in   输出文件:sparty.out   简单对比时间限制:3 s   内存限制:16 MB 译: zqzas N ...

  7. Java 用链表实现栈和队列

    栈 是一种基于后进先出(LIFO)策略的集合类型.当邮件在桌上放成一叠时,就能用栈来表示.新邮件会放在最上面,当你要看邮件时,会一封一封从上到下阅读.栈的顶部称为栈顶,所有操作都在栈顶完成. 前面提到 ...

  8. dfs序 + 树状数组

    You are given a tree, it’s root is p, and the node is numbered from 1 to n. Now define f(i) as the n ...

  9. git 控制版本

    git:版本控制工具 1.进入想要进行版本控制项目的文件夹内 2.右击->Git Bash Here 3.初始化 git init 会多出一个.git隐藏文件夹 4.查看文件状体 git sta ...

  10. ValidationAttribute特性的截图