BZOJ 2434 阿狸的打字机(ac自动机+dfs序+树状数组)
题意
给你一些串,还有一些询问
问你第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序+树状数组)的更多相关文章
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...
- NOI 2011 阿狸的打字机 (AC自动机+dfs序+树状数组)
题目大意:略(太长了不好描述) 良心LOJ传送门 先对所有被打印的字符串建一颗Trie树 观察数据范围,并不能每次打印都从头到尾暴力建树,而是每遍历到一个字符就在Trie上插入这个字符,然后记录每次打 ...
- BZOJ2434[Noi2011]阿狸的打字机——AC自动机+dfs序+树状数组
题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小 ...
- 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组
[BZOJ2434][NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P ...
- BZOJ_2434_[NOI2011]_阿狸的打字机_(AC自动机+dfs序+树状数组)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2434 给出\(n\)个字符串,\(m\)个询问,对于第\(i\)个询问,求第\(x_i\)个字 ...
- BZOJ2434: [NOI2011]阿狸的打字机(AC自动机+dfs序+树状数组)
[NOI2011]阿狸的打字机 题目链接:https://www.luogu.org/problemnew/show/P2414 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. ...
- CodeForces -163E :e-Government (AC自动机+DFS序+树状数组)
The best programmers of Embezzland compete to develop a part of the project called "e-Governmen ...
- BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组
BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组 Description Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是 ...
- [NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]
题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 ...
随机推荐
- 一个.NET程序员 "2019" 跳槽3次的悲惨故事
2019年是值得深思的一年,在找工作上没有那么用心,导致碌碌无为,在这里我建议大家找工作的时候不要太着急...要不然会被逼疯的,一定不能被“工作”挑,一定要做到挑"工作".:那我就 ...
- 6.7 Mapreduce作业流JobControl和Oozie
1.1 Mapreduce作业流JobControl和Oozie 更复杂的任务,需要多个mapreduce作业,形成作业流,而不是增加map和reduce的复杂度.复杂问题,可以用高级语言pig.h ...
- Spring中Bean的实例化与DI的过程
引言 前文我们介绍了关于如何学习Spring的源码以及解析了spring中加载配置文件注册Beandefinition的过程.今天我们继续学习DI的过程. 创建实例和DI过程 IOC和DI都是对spr ...
- Linux环境下详细讲解部署MySQL5.7版本
说明: 在本人写作这篇安装MySQL文章时,虽然MySQL已经发布到8.0.17版本,但对于行业来说,主力版本依然是5.7版本.目前在Linux环境默认安装时,大部分已经默认安装到8版本了,所以本人特 ...
- 【转】分布式服务框架 Zookeeper -- 管理分布式环境中的数据
Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理 ...
- Spring Boot2 系列教程 (二) | 第一个 SpringBoot 工程详解
微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 哎呦喂,按照以往的惯例今天周六我的安排应该是待在家学学猫叫啥的.但是今年这种日子就可能一去不复返了,没法办法啊.前 ...
- Big Event
666DHG的大事记 2019.4.15 \(\text{ }\text{ }\text{ }\)注册洛谷 2019.8.10 \(\text{ }\text{ }\text{ }\)Luogu\(\ ...
- 【java面试】数据库篇
1.SQL语句分为哪几种? SQL语句主要可以划分为以下几类: DDL(Data Definition Language):数据定义语言,定义对数据库对象(库.表.列.索引)的操作. 包括:CREAT ...
- NHibernate 初识(0)
参考资料: http://nhibernate.info/
- selenium,测试套件的使用
学习 selenium-webdriver 已经一段时间了,最近学习到,测试用例的批量执行,和测试套件的使用,有点自己的理解,不晓得对不对,希望大家指正! 写一个测试用例 baidu.py c ...