题目描述

Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串S_x。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。

输入

第1行,一个数n;
接下来n行,每行一个字符串表示S_i;
下一行,一个数q;
接下来q行,每行一个操作,格式见题目描述。

输出

对于每一个Alice的询问,帮Bob输出答案。

样例输入

3
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3

样例输出

1
2
1

提示

【数据范围】
1 <= n,q <= 100000;
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000;
字符串都由小写英文字母组成。

这道题和bzoj2434都是很好的AC自动机练习题,都利用了fail树的性质来解决字符串问题。首先要知道一点:如果x串是y串的子串,那x串一定是y串一个前缀的后缀。我们知道AC自动机上每个点表示这个点到根节点的字符串。这道题的大体思路是把S集合建成AC自动机,每添加一个T集合中的字符串P就把他在AC自动机上跑一遍,把所有遍历的点及它们fail指针能达到的所有点的答案数都加1(遍历的点自然是这个串的子串,fail指针能达到的点都是遍历的点的后缀,当然也是加入的串的子串),然后查询。利用fail树的性质可以发现,每次插入一个串P,除了遍历的点,其他答案要加1的点都是这些点在fail树上的祖先,因此只要用树上差分将遍历的点+1就把所有答案要加的点都加了。但这样有的点就会被多加好几次,每次插入却最多只能把每个点加一次,因此,要把遍历的点按dfs序排序,然后将相邻两个点的lca到根节点答案都-1(也就是用差分将lca-1)就去重了(因为按dfs序相邻两个点最近,lca也最深)。查询时用树状数组在dfs序上区间求和就行了。

最后附上代码。

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int sum;
int num;
int cnt;
int tot;
int opt;
int g[100010];
int k[2000010];
int v[2000010];
int l[2000010];
int r[2000010];
int d[2000010];
char s[2000010];
int to[2000010];
int head[2000010];
int next[2000010];
int fail[2000010];
int f[23][2000010];
int a[2000010][26];
void add(int x,int y)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void change(int x,int val)
{
for(int i=x;i<=num;i+=i&-i)
{
v[i]+=val;
}
}
int ask(int x)
{
int res=0;
for(int i=x;i;i-=i&-i)
{
res+=v[i];
}
return res;
}
void build(char *s,int c)
{
int now=0;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
if(!a[now][x])
{
a[now][x]=++cnt;
}
now=a[now][x];
}
g[c]=now;
}
void getfail()
{
queue<int>q;
for(int i=0;i<26;i++)
{
if(a[0][i])
{
q.push(a[0][i]);
fail[a[0][i]]=0;
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
add(fail[now],now);
f[0][now]=fail[now];
for(int i=0;i<26;i++)
{
if(a[now][i])
{
fail[a[now][i]]=a[fail[now]][i];
q.push(a[now][i]);
}
else
{
a[now][i]=a[fail[now]][i];
}
}
}
}
bool cmp(int x,int y)
{
return l[x]<l[y];
}
void dfs(int x)
{
l[x]=++num;
d[x]=d[f[0][x]]+1;
for(int i=1;i<=21;i++)
{
f[i][x]=f[i-1][f[i-1][x]];
}
for(int i=head[x];i;i=next[i])
{
dfs(to[i]);
}
r[x]=num;
}
int lca(int x,int y)
{
if(d[x]<d[y])
{
swap(x,y);
}
int dep=d[x]-d[y];
for(int i=0;i<=21;i++)
{
if(((1<<i)&dep)!=0)
{
x=f[i][x];
}
}
if(x==y)
{
return x;
}
for(int i=21;i>=0;i--)
{
if(f[i][x]!=f[i][y])
{
x=f[i][x];
y=f[i][y];
}
}
return f[0][x];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
build(s,i);
}
getfail();
dfs(0);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%s",s);
int len=strlen(s);
int now=0;
int x=0;
for(int j=0;j<len;j++)
{
now=a[now][s[j]-'a'];
k[++x]=now;
}
sort(k+1,k+1+x,cmp);
for(int j=1;j<=x;j++)
{
change(l[k[j]],1);
} for(int j=1;j<x;j++)
{
change(l[lca(k[j],k[j+1])],-1);
}
}
else
{
scanf("%d",&sum);
printf("%d\n",ask(r[g[sum]])-ask(l[g[sum]]-1));
}
}
}

BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并的更多相关文章

  1. Codeforces 1111E DP + 树状数组 + LCA + dfs序

    题意:给你一颗树,有q次询问,每次询问给你若干个点,这些点可以最多分出m组,每组要满足两个条件:1:每组至少一个点,2:组内的点不能是组内其它点的祖先,问这样的分组能有多少个? 思路:https:// ...

  2. BZOJ 3881 [COCI2015]Divljak (Trie图+Fail树+树链的并+树状数组维护dfs序)

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

  3. (好题)树状数组+离散化+DFS序+离线/莫队 HDOJ 4358 Boring counting

    题目传送门 题意:给你一棵树,树上的每个节点都有树值,给m个查询,问以每个点u为根的子树下有多少种权值恰好出现k次. 分析:首先要对权值离散化,然后要将树形转换为线形,配上图:.然后按照右端点从小到大 ...

  4. 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序

    3881: [Coci2015]Divljak Time Limit: 20 Sec  Memory Limit: 768 MBSubmit: 508  Solved: 158[Submit][Sta ...

  5. 【BZOJ4999】This Problem Is Too Simple! 离线+树状数组+LCA

    [BZOJ4999]This Problem Is Too Simple! Description 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2 ...

  6. 【BZOJ】2819: Nim(树链剖分 / lca+dfs序+树状数组)

    题目 传送门:QWQ 分析 先敲了个树链剖分,发现无法AC(其实是自己弱,懒得debug.手写栈) 然后去学了学正解 核心挺好理解的,$ query(a) $是$ a $到根的异或和. 答案就是$ l ...

  7. Ultra-QuickSort(树状数组求逆序对数)

    Ultra-QuickSort 题目链接:http://poj.org/problem?id=2299 Time Limit: 7000MS   Memory Limit: 65536K Total ...

  8. hdu4605 树状数组+离散化+dfs

    Magic Ball Game Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  9. hdu2838树状数组解逆序

    离散化和排序后的序号问题搞得我实在是头痛 不过树状数组解逆序和偏序一类问题真的好用 更新:hdu的数据弱的真实,我交上去错的代价也对了.. 下面的代码是错的 /* 每个点的贡献度=权值*在这个点之前的 ...

随机推荐

  1. linux源码分析 - 进程

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 最近在回想一些知识点的时候,觉得对进程这一块有些模糊,特别写一篇随笔对进程信息进行巩固和复习. 程序和进程 以我 ...

  2. 解决 在Android开发上使用KSOAP2上传大图片到服务器经常报错的问题

    原文首发我的主力博客 http://anforen.com/wp/2017/04/android_ksoap2_unexpected_type_position_end_document_null_j ...

  3. 计算2个时间之间经过多少Ticks

    Ticks是一个周期,存储的是一百纳秒,换算为秒,一千万分之一秒.我们需要计算2个时间之间,经过多少Ticks,可以使用下面的方法来实现,使用2个时间相减. 得到结果为正数,是使用较晚的时间减去较早的 ...

  4. Luogu P3455 [POI2007]ZAP-Queries

    由于之前做了Luogu P2257 YY的GCD,这里的做法就十分套路了. 建议先看上面一题的推导,这里的话就略去一些共性的地方了. 还是和之前一样设: \[f(d)=\sum_{i=1}^a \su ...

  5. STM32串口打印输出乱码的解决办法

    前言 最近在试用uFUN开发板,下载配套的Demo程序,串口数据输出正常,当使用另一个模板工程,调用串口printf调试功能时,输出的却是乱码,最后发现是外部晶振频率不一样.很多STM32开发板都是使 ...

  6. 《坦克世界》1.0+:使用 CPU 优化的图形和物理丰富用户体验

    本文以<坦克世界>为例,介绍 Wargaming 使用 CPU 多核和 CPU 单指令多数据 (SIMD) 功能显著提升游戏沉浸式体验的创新方法.我们以英特尔® 线程构建模块(英特尔® T ...

  7. mysql 编码和汉字存储占用字节问题的探索

    MySql 5.5 之前,UTF8 编码只支持1-3个字节,只支持BMP这部分的unicode编码区,BMP是从哪到哪?基本就是 0000 ~ FFFF 这一区. 从MySQL 5.5 开始,可支持4 ...

  8. 四则运算-ppt演示

     

  9. Beta之后的想法

    软件工程如果没选实践,单纯在理论课上面对教条化的理论,这些理论都是很有指导意义的,但没有实践课带来的切实的多人团队合作开发项目的实际体会,很难能领会到其中的深意.知行合一,才能发现软件工程里的知识都是 ...

  10. 运用PDO存储将图片、音频文件存入数据库

    在数据库中创建表格的时候,有一个字段为image,用来保存图片,那么其类型就是blob,关于blob,百度百科是这样描述的 BLOB (binary large object),二进制大对象,是一个可 ...